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
POST https://www.wiseyield.co/api/api-keys
Dashboard-only endpoint. Called from the WiseYield UI with browser cookies (Clerk session). Not callable from a server-to-server integration today. To mint API keys, sign in to the dashboard and use Settings → API Keys → Create API Key .
Summit-tier only. API key creation requires an active Summit subscription. Requests from users on Seed / Sprout / Harvest / Grove / Trial tiers return 403 TIER_INSUFFICIENT. See Subscription tiers for what’s included at each tier.
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.
Request Body
User-friendly name for the API key (e.g., “Production Server”, “Mobile App”)
Array of permission scopes. At least one scope required. Available Scopes :
farms:read, farms:write
crops:read, crops:write
fields:read, fields:write
library:read, library:write
market:read
analytics:read
tasks:read, tasks:write
team:read, team:write
webhooks:read, webhooks:write
all (full access)
Environment type: live (production) or test (development)
Maximum API requests per hour (1-100,000)
ISO 8601 datetime when key should expire (optional, no expiration if omitted)
Custom metadata to attach to the key (optional)
Response
Returns the newly created API key object with the full key .
Unique identifier (format: key_{nanoid})
⚠️ CRITICAL : Full API key (shown ONLY ONCE, never retrievable again)Format: wy_{environment}_{48_hex_characters}
First 16 characters for identification
Masked version for display
Environment: live or test
Array of granted permission scopes
ISO 8601 timestamp of creation
ISO 8601 timestamp of last update
Expiration timestamp (null if no expiration)
Custom metadata (includes userAgent automatically)
cURL
JavaScript
JavaScript (With Secure Storage)
Python
curl -X POST https://www.wiseyield.co/api/api-keys \
-H "Authorization: Bearer {CLERK_SESSION_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "Production API Key",
"environment": "live",
"scopes": ["farms:read", "farms:write", "crops:read", "crops:write"],
"rateLimit": 5000,
"metadata": {
"application": "web-dashboard",
"version": "1.0.0"
}
}'
201 Created
400 Bad Request (Missing Name)
400 Bad Request (Invalid Scopes)
400 Bad Request (Key Limit Exceeded)
400 Bad Request (Invalid Environment)
400 Bad Request (Invalid Rate Limit)
400 Bad Request (Invalid Expiry)
401 Unauthorized
403 Tier Insufficient
500 Internal Server Error
{
"id" : "key_abc123xyz789def456" ,
"key" : "wy_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2" ,
"keyPrefix" : "wy_live_a1b2c3d4" ,
"keyPreview" : "wy_live_a1b2c3d4...****" ,
"name" : "Production API Key" ,
"environment" : "live" ,
"scopes" : [ "farms:read" , "farms:write" , "crops:read" , "crops:write" ],
"rateLimit" : 5000 ,
"isActive" : true ,
"usageCount" : 0 ,
"lastUsedAt" : null ,
"createdAt" : "2025-10-30T10:00:00.000Z" ,
"updatedAt" : "2025-10-30T10:00:00.000Z" ,
"expiresAt" : null ,
"metadata" : {
"application" : "web-dashboard" ,
"version" : "1.0.0" ,
"userAgent" : "Mozilla/5.0..."
}
}
Errors
Bad Request - Validation failed (missing fields, invalid scopes, limit exceeded)
Unauthorized - User not signed in
Internal Server Error - Failed to create API key
How It Works
Creation Flow :
Authentication : Verifies user is signed in
Validation : Checks all required fields and constraints:
Name and scopes are required
Scopes must be from valid list
Environment must be “live” or “test”
Rate limit: 1-100,000
Expiry date must be in future
Limit Check : Ensures user has < 10 active keys
Key Generation :
Generates 24 random bytes (48 hex chars)
Formats as wy_{environment}_{random}
Creates SHA-256 hash for storage
Extracts first 16 chars as prefix
Database Insert : Saves key with metadata
Audit Log : Records creation for security tracking
Response : Returns full key (only time it’s shown )
Security :
Only SHA-256 hash stored in database
Full key never retrievable after creation
Prefix stored for identification
User-agent automatically captured in metadata
API Key Structure
wy_{environment}_{48_hexadecimal_characters}
Example (Live) :
wy_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2
Example (Test) :
wy_test_x9y8z7w6v5u4t3s2r1q0p9o8n7m6l5k4j3i2h1g0f9e8
Components :
wy_ - WiseYield identifier
{environment} - live or test
{random} - 48 hexadecimal characters (cryptographically secure)
Storage
Database :
keyHash - SHA-256 hash of full key
keyPrefix - First 16 characters (for display/identification)
Never Stored :
Full API key value (security best practice)
Scopes Reference
Farm Management
Crop Management
Field Management
Analytics
Task Management
Team Management
Webhooks
Full Access
Use Cases
Production Key with Limited Scopes
async function createProductionKey () {
const response = await fetch ( '/api/api-keys' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ clerkSessionToken } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
name: 'Production Server' ,
environment: 'live' ,
scopes: [
'farms:read' ,
'crops:read' ,
'fields:read' ,
'analytics:read'
], // Read-only access
rateLimit: 10000 ,
metadata: {
server: 'prod-api-01' ,
region: 'us-east-1'
}
})
});
const key = await response . json ();
// ⚠️ Save to secure storage
await storeInVault ( 'WISEYIELD_API_KEY' , key . key );
return key ;
}
Test Key with Full Access
async function createTestKey () {
const response = await fetch ( '/api/api-keys' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ clerkSessionToken } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
name: 'Local Development' ,
environment: 'test' ,
scopes: [ 'all' ], // Full access for testing
rateLimit: 1000
})
});
const key = await response . json ();
// Can safely add to .env.local for development
console . log ( 'Add to .env.local:' );
console . log ( `WISEYIELD_API_KEY= ${ key . key } ` );
return key ;
}
Temporary Key with Expiration
async function createTemporaryKey ( daysUntilExpiry = 30 ) {
const expiresAt = new Date ();
expiresAt . setDate ( expiresAt . getDate () + daysUntilExpiry );
const response = await fetch ( '/api/api-keys' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ clerkSessionToken } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
name: `Temporary Access ( ${ daysUntilExpiry } d)` ,
environment: 'live' ,
scopes: [ 'farms:read' , 'crops:read' ],
expiresAt: expiresAt . toISOString (),
rateLimit: 500 ,
metadata: {
purpose: 'temporary-integration' ,
requestedBy: 'partner-xyz'
}
})
});
const key = await response . json ();
// Share with partner
console . log ( 'Temporary key created' );
console . log ( `Expires: ${ new Date ( key . expiresAt ). toLocaleDateString () } ` );
return key ;
}
Mobile App Key
async function createMobileAppKey () {
const response = await fetch ( '/api/api-keys' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ clerkSessionToken } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
name: 'iOS App v2.1' ,
environment: 'live' ,
scopes: [
'farms:read' ,
'crops:read' ,
'crops:write' ,
'tasks:read' ,
'tasks:write'
],
rateLimit: 2000 ,
metadata: {
platform: 'ios' ,
appVersion: '2.1.0' ,
buildNumber: '142'
}
})
});
const key = await response . json ();
// Embed in app configuration
return key ;
}
Best Practices
Save Immediately : Copy the full key to secure storage as soon as it’s created. It cannot be retrieved later.
Principle of Least Privilege : Grant only the scopes needed for the specific use case.
Use Test Keys for Development : Create test environment keys for local development and testing.
Set Expiration : For temporary access, always set an expiresAt date.
Descriptive Names : Use clear, descriptive names that indicate purpose and environment.
Metadata for Tracking : Use metadata to track which application, server, or user is using each key.
One-Time Display : The full API key is shown ONLY once. Save it immediately or lose it forever.
10 Key Limit : Maximum 10 active API keys per user. Revoke unused keys before creating new ones.
Never Commit Keys : Never commit API keys to version control. Use environment variables.
Rotate Regularly : Create new keys and revoke old ones periodically for security.
Security Best Practices
Key Storage :
# ✅ GOOD: Environment variables
WISEYIELD_API_KEY = wy_live_abc123...
# ✅ GOOD: Secret management services
aws secretsmanager create-secret --name wiseyield-api-key
# ❌ BAD: Hardcoded in code
const apiKey = "wy_live_abc123..."
# ❌ BAD: Committed to git
git add .env # If .env contains keys!
Key Rotation :
async function rotateApiKey ( oldKeyId ) {
// 1. Create new key
const newKey = await createApiKey ({
name: 'Production Server (Rotated)' ,
environment: 'live' ,
scopes: [ ... ],
rateLimit: 10000
});
// 2. Update application config
await updateEnvVar ( 'WISEYIELD_API_KEY' , newKey . key );
// 3. Test new key
await testApiKey ( newKey . key );
// 4. Revoke old key
await revokeApiKey ( oldKeyId );
}
Scope Management :
// ✅ GOOD: Minimal scopes
const readOnlyKey = {
scopes: [ 'farms:read' , 'crops:read' ]
};
// ⚠️ CAUTION: Broad access
const writeKey = {
scopes: [ 'farms:write' , 'crops:write' , 'fields:write' ]
};
// ⚠️ DANGER: Full access
const adminKey = {
scopes: [ 'all' ] // Only for trusted applications
};
Integration Examples
API Key Creation Modal
function CreateApiKeyModal ({ isOpen , onClose , onCreated }) {
const [ formData , setFormData ] = useState ({
name: '' ,
environment: 'live' ,
scopes: [],
rateLimit: 1000 ,
expiresAt: '' ,
});
const [ creating , setCreating ] = useState ( false );
const [ createdKey , setCreatedKey ] = useState ( null );
async function handleCreate () {
setCreating ( true );
try {
const response = await fetch ( '/api/api-keys' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ clerkSessionToken } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
... formData ,
expiresAt: formData . expiresAt || undefined
})
});
if ( ! response . ok ) {
const error = await response . json ();
toast . error ( error . message || 'Failed to create API key' );
return ;
}
const newKey = await response . json ();
setCreatedKey ( newKey );
toast . success ( 'API key created' );
onCreated ?.( newKey );
} finally {
setCreating ( false );
}
}
if ( createdKey ) {
return (
< Modal open = { isOpen } onClose = { onClose } >
< ModalTitle > API Key Created </ ModalTitle >
< ModalContent >
< Alert severity = "warning" >
< AlertTitle > Save This Key Now! </ AlertTitle >
< p > This is the only time you'll see the full key. Copy it to a secure location. </ p >
</ Alert >
< div className = "my-4" >
< label className = "block text-sm font-medium mb-2" > API Key </ label >
< div className = "flex gap-2" >
< code className = "flex-1 p-3 bg-gray-100 rounded font-mono text-sm break-all" >
{ createdKey . key }
</ code >
< button
onClick = { () => {
navigator . clipboard . writeText ( createdKey . key );
toast . success ( 'Copied to clipboard' );
} }
className = "btn btn-secondary"
>
Copy
</ button >
</ div >
</ div >
< div className = "text-sm text-gray-600" >
< div >< strong > Name: </ strong > { createdKey . name } </ div >
< div >< strong > Environment: </ strong > { createdKey . environment } </ div >
< div >< strong > Rate Limit: </ strong > { createdKey . rateLimit } /hour </ div >
</ div >
</ ModalContent >
< ModalActions >
< button onClick = { onClose } className = "btn btn-primary" >
I've Saved My Key
</ button >
</ ModalActions >
</ Modal >
);
}
return (
< Modal open = { isOpen } onClose = { onClose } >
< ModalTitle > Create API Key </ ModalTitle >
< ModalContent >
< form className = "space-y-4" >
< div >
< label className = "block text-sm font-medium mb-2" > Name * </ label >
< input
type = "text"
value = { formData . name }
onChange = { ( e ) => setFormData ({ ... formData , name: e . target . value }) }
placeholder = "e.g., Production Server"
required
/>
</ div >
< div >
< label className = "block text-sm font-medium mb-2" > Environment </ label >
< select
value = { formData . environment }
onChange = { ( e ) => setFormData ({ ... formData , environment: e . target . value }) }
>
< option value = "live" > Live (Production) </ option >
< option value = "test" > Test (Development) </ option >
</ select >
</ div >
< div >
< label className = "block text-sm font-medium mb-2" > Scopes * </ label >
< ScopeSelector
selected = { formData . scopes }
onChange = { ( scopes ) => setFormData ({ ... formData , scopes }) }
/>
</ div >
< div >
< label className = "block text-sm font-medium mb-2" >
Rate Limit (requests/hour)
</ label >
< input
type = "number"
value = { formData . rateLimit }
onChange = { ( e ) => setFormData ({ ... formData , rateLimit: parseInt ( e . target . value ) }) }
min = "1"
max = "100000"
/>
</ div >
< div >
< label className = "block text-sm font-medium mb-2" >
Expiration Date (optional)
</ label >
< input
type = "datetime-local"
value = { formData . expiresAt }
onChange = { ( e ) => setFormData ({ ... formData , expiresAt: e . target . value }) }
/>
</ div >
</ form >
</ ModalContent >
< ModalActions >
< button onClick = { onClose } disabled = { creating } >
Cancel
</ button >
< button
onClick = { handleCreate }
disabled = { ! formData . name || formData . scopes . length === 0 || creating }
className = "btn btn-primary"
>
{ creating ? 'Creating...' : 'Create API Key' }
</ button >
</ ModalActions >
</ Modal >
);
}