Documentation Index Fetch the complete documentation index at: https://docs.wiseyield.co/llms.txt
Use this file to discover all available pages before exploring further.
Endpoint
DELETE https://www.wiseyield.co/api/api-keys/{key_id}
Dashboard-only endpoint. Called from the WiseYield UI with browser cookies (Clerk session). Not callable from a server-to-server integration today. Revoke keys from Settings → API Keys in the dashboard .
Authentication
Requires a signed-in WiseYield session. The Bearer examples below show the response wire format but are not directly callable outside the browser session.
Path Parameters
Unique identifier of the API key to revoke (format: key_{nanoid})
Response
Returns success confirmation.
Always true on successful revocation
Success message: “API key revoked successfully”
cURL
JavaScript
JavaScript (With Confirmation)
Python
curl -X DELETE https://www.wiseyield.co/api/api-keys/key_abc123xyz789 \
-H "Authorization: Bearer {CLERK_SESSION_TOKEN}"
200 Success
401 Unauthorized
404 Not Found
500 Internal Server Error
{
"success" : true ,
"message" : "API key revoked successfully"
}
Errors
Unauthorized - User not signed in
Not Found - API key not found, already revoked, or belongs to different user
Internal Server Error - Failed to revoke API key
How It Works
Revocation Flow :
Authentication : Verifies user is signed in
Ownership Check : Finds API key where:
id matches provided key_id
userId matches authenticated user
revokedAt is null (not already revoked)
Soft Delete : Updates API key:
revokedAt: null → current timestamp
isActive: true → false
updatedAt: current timestamp
Audit Log : Records revocation for security tracking
Immediate Effect : Key becomes invalid for all API requests
Why Soft Delete? : Maintains audit trail while preventing further use. Revoked keys stay in database for historical tracking but can never be reactivated.
Post-Revocation :
Key will not authenticate any API requests
Key excluded from list endpoints (filters by revokedAt IS NULL)
Cannot be restored (must create new key)
Historical usage data preserved
Security Implications
What Happens Instantly :
✅ All active API requests using this key will fail with 401 Unauthorized
✅ Key removed from active keys list
✅ Rate limit tracking stops
✅ Usage tracking stops
What’s Preserved :
✅ Historical usage data (usageCount, lastUsedAt)
✅ Audit logs showing when key was created and revoked
✅ Metadata and configuration
Impact on Applications
Applications Using Revoked Key :
// After revocation, this will fail
const response = await fetch ( 'https://www.wiseyield.co/api/farms' , {
headers: { 'Authorization' : 'Bearer wy_live_revoked_key' }
});
// Response: 401 Unauthorized
{
"error" : "Invalid API key"
}
Recommended Actions :
Update application with new API key
Monitor error logs for failed requests
Notify team members using the key
Use Cases
Revoke Compromised Key
async function revokeCompromisedKey ( keyId ) {
// Immediately revoke
await fetch ( `/api/api-keys/ ${ keyId } ` , {
method: 'DELETE' ,
headers: { 'Authorization' : `Bearer ${ clerkSessionToken } ` }
});
// Create replacement key
const newKey = await fetch ( '/api/api-keys' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ clerkSessionToken } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
name: 'Production Server (Replacement)' ,
environment: 'live' ,
scopes: [ ... ], // Same scopes as revoked key
rateLimit: 10000
})
}). then ( r => r . json ());
// Alert: Key revoked, update app config
console . log ( 'Old key revoked, new key:' , newKey . key );
return newKey ;
}
Revoke Unused Keys
async function revokeUnusedKeys ( daysInactive = 90 ) {
// Get all keys
const { data : keys } = await fetch ( '/api/api-keys' ). then ( r => r . json ());
const cutoffDate = new Date ();
cutoffDate . setDate ( cutoffDate . getDate () - daysInactive );
// Find keys not used in X days
const unusedKeys = keys . filter ( key => {
if ( ! key . lastUsedAt ) return true ; // Never used
const lastUsed = new Date ( key . lastUsedAt );
return lastUsed < cutoffDate ;
});
console . log ( `Found ${ unusedKeys . length } unused keys` );
// Revoke each
for ( const key of unusedKeys ) {
console . log ( `Revoking: ${ key . name } ` );
await fetch ( `/api/api-keys/ ${ key . id } ` , {
method: 'DELETE' ,
headers: { 'Authorization' : `Bearer ${ clerkSessionToken } ` }
});
}
console . log ( 'Cleanup complete' );
}
Revoke Expired Keys
async function revokeExpiredKeys () {
const { data : keys } = await fetch ( '/api/api-keys' ). then ( r => r . json ());
const now = new Date ();
const expiredKeys = keys . filter ( key => {
if ( ! key . expiresAt ) return false ;
return new Date ( key . expiresAt ) < now ;
});
console . log ( `Found ${ expiredKeys . length } expired keys` );
for ( const key of expiredKeys ) {
await fetch ( `/api/api-keys/ ${ key . id } ` , {
method: 'DELETE' ,
headers: { 'Authorization' : `Bearer ${ clerkSessionToken } ` }
});
console . log ( `Revoked expired key: ${ key . name } ` );
}
}
Rotate API Key
async function rotateApiKey ( oldKeyId ) {
// Get old key details
const { data : keys } = await fetch ( '/api/api-keys' ). then ( r => r . json ());
const oldKey = keys . find ( k => k . id === oldKeyId );
if ( ! oldKey ) {
throw new Error ( 'Key not found' );
}
// Create new key with same configuration
const newKeyResponse = await fetch ( '/api/api-keys' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ clerkSessionToken } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
name: ` ${ oldKey . name } (Rotated)` ,
environment: oldKey . environment ,
scopes: oldKey . scopes ,
rateLimit: oldKey . rateLimit ,
metadata: {
... oldKey . metadata ,
rotatedFrom: oldKeyId ,
rotatedAt: new Date (). toISOString ()
}
})
});
const newKey = await newKeyResponse . json ();
console . log ( 'New key created:' , newKey . keyPreview );
console . log ( 'Full key (save now):' , newKey . key );
// Update application configuration
await updateEnvVar ( 'WISEYIELD_API_KEY' , newKey . key );
// Test new key
const testResponse = await fetch ( 'https://www.wiseyield.co/api/farms' , {
headers: { 'Authorization' : `Bearer ${ newKey . key } ` }
});
if ( ! testResponse . ok ) {
throw new Error ( 'New key failed validation' );
}
// Revoke old key
await fetch ( `/api/api-keys/ ${ oldKeyId } ` , {
method: 'DELETE' ,
headers: { 'Authorization' : `Bearer ${ clerkSessionToken } ` }
});
console . log ( 'Old key revoked' );
console . log ( 'Rotation complete' );
return newKey ;
}
Bulk Revoke Test Keys
async function revokeAllTestKeys () {
const { data : keys } = await fetch ( '/api/api-keys' ). then ( r => r . json ());
const testKeys = keys . filter ( key => key . environment === 'test' );
console . log ( `Revoking ${ testKeys . length } test keys` );
const results = await Promise . allSettled (
testKeys . map ( key =>
fetch ( `/api/api-keys/ ${ key . id } ` , {
method: 'DELETE' ,
headers: { 'Authorization' : `Bearer ${ clerkSessionToken } ` }
})
)
);
const succeeded = results . filter ( r => r . status === 'fulfilled' ). length ;
console . log ( `Revoked ${ succeeded } / ${ testKeys . length } test keys` );
}
Best Practices
Confirm Before Revoke : Always show confirmation dialog before revoking keys.
Create Before Revoke : When rotating, create new key and test it before revoking the old one.
Monitor Usage : Check lastUsedAt to identify safe-to-revoke keys.
Audit Regularly : Review and revoke unused keys quarterly.
Immediate Revocation : Key becomes invalid instantly. All apps using it will lose access.
Cannot Undo : Revocation is permanent. Must create new key to restore access.
No Grace Period : There is no transition period. Applications will fail immediately.
Integration Examples
function RevokeApiKeyButton ({ apiKey }) {
const [ revoking , setRevoking ] = useState ( false );
async function handleRevoke () {
const confirmed = await confirmDialog ({
title: 'Revoke API Key?' ,
message : (
< div >
< p > Are you sure you want to revoke < strong > { apiKey . name } </ strong > ? </ p >
< p className = "text-red-600 mt-2" >
This action cannot be undone. All applications using this key will immediately lose access.
</ p >
{ apiKey . lastUsedAt && (
< p className = "text-sm text-gray-600 mt-2" >
Last used: { formatDistanceToNow ( new Date ( apiKey . lastUsedAt )) } ago
</ p >
) }
</ div >
),
confirmText: 'Yes, Revoke' ,
confirmButtonClass: 'btn-danger' ,
});
if ( ! confirmed ) return ;
setRevoking ( true );
try {
const response = await fetch ( `/api/api-keys/ ${ apiKey . id } ` , {
method: 'DELETE' ,
headers: { 'Authorization' : `Bearer ${ clerkSessionToken } ` }
});
if ( ! response . ok ) {
const error = await response . json ();
toast . error ( error . message );
return ;
}
toast . success ( 'API key revoked' );
// Remove from UI
onRevoked ?.( apiKey . id );
} finally {
setRevoking ( false );
}
}
return (
< button
onClick = { handleRevoke }
disabled = { revoking }
className = "btn btn-danger-outline btn-sm"
>
{ revoking ? 'Revoking...' : 'Revoke' }
</ button >
);
}
API Keys Management Table
function ApiKeysManagementTable () {
const [ keys , setKeys ] = useState ([]);
async function handleRevoke ( keyId ) {
const response = await fetch ( `/api/api-keys/ ${ keyId } ` , {
method: 'DELETE' ,
headers: { 'Authorization' : `Bearer ${ clerkSessionToken } ` }
});
if ( response . ok ) {
toast . success ( 'Key revoked' );
setKeys ( prev => prev . filter ( k => k . id !== keyId ));
}
}
return (
< table >
< thead >
< tr >
< th > Name </ th >
< th > Key </ th >
< th > Environment </ th >
< th > Usage </ th >
< th > Last Used </ th >
< th > Actions </ th >
</ tr >
</ thead >
< tbody >
{ keys . map (( key ) => (
< tr key = { key . id } >
< td > { key . name } </ td >
< td >< code > { key . keyPreview } </ code ></ td >
< td >
< Badge color = { key . environment === 'live' ? 'green' : 'blue' } >
{ key . environment }
</ Badge >
</ td >
< td > { key . usageCount . toLocaleString () } </ td >
< td >
{ key . lastUsedAt
? formatDistanceToNow ( new Date ( key . lastUsedAt ), { addSuffix: true })
: 'Never' }
</ td >
< td >
< RevokeApiKeyButton
apiKey = { key }
onRevoked = { () => handleRevoke ( key . id ) }
/>
</ td >
</ tr >
)) }
</ tbody >
</ table >
);
}
Cleanup Unused Keys Alert
function UnusedKeysAlert () {
const [ unusedKeys , setUnusedKeys ] = useState ([]);
useEffect (() => {
async function checkUnused () {
const { data : keys } = await fetch ( '/api/api-keys' ). then ( r => r . json ());
const sixtyDaysAgo = new Date ();
sixtyDaysAgo . setDate ( sixtyDaysAgo . getDate () - 60 );
const unused = keys . filter ( key => {
if ( ! key . lastUsedAt ) return true ;
return new Date ( key . lastUsedAt ) < sixtyDaysAgo ;
});
setUnusedKeys ( unused );
}
checkUnused ();
}, []);
async function revokeAll () {
const confirmed = confirm ( `Revoke ${ unusedKeys . length } unused keys?` );
if ( ! confirmed ) return ;
for ( const key of unusedKeys ) {
await fetch ( `/api/api-keys/ ${ key . id } ` , {
method: 'DELETE' ,
headers: { 'Authorization' : `Bearer ${ clerkSessionToken } ` }
});
}
toast . success ( `Revoked ${ unusedKeys . length } keys` );
setUnusedKeys ([]);
}
if ( unusedKeys . length === 0 ) return null ;
return (
< Alert severity = "info" >
< AlertTitle > Unused API Keys </ AlertTitle >
< p > You have { unusedKeys . length } API key(s) that haven't been used in 60+ days. </ p >
< button onClick = { revokeAll } className = "btn btn-sm mt-2" >
Revoke All Unused Keys
</ button >
</ Alert >
);
}