curl -X GET https://www.wiseyield.co/api/api-keys \
-H "Authorization: Bearer {CLERK_SESSION_TOKEN}"
// Using Clerk session
const response = await fetch(
'https://www.wiseyield.co/api/api-keys',
{
method: 'GET',
headers: {
'Authorization': `Bearer ${clerkSessionToken}`
}
}
);
const { data, total } = await response.json();
console.log(`You have ${total} active API keys`);
data.forEach(key => {
console.log(`${key.name}: ${key.keyPreview}`);
});
async function listApiKeys() {
try {
const response = await fetch('/api/api-keys', {
method: 'GET',
headers: {
'Authorization': `Bearer ${clerkSessionToken}`
}
});
if (!response.ok) {
const error = await response.json();
if (response.status === 401) {
router.push('/sign-in');
return;
}
toast.error(error.error || 'Failed to load API keys');
return;
}
const { data, total } = await response.json();
return { keys: data, total };
} catch (error) {
toast.error('Network error. Please try again.');
}
}
import requests
response = requests.get(
'https://www.wiseyield.co/api/api-keys',
headers={'Authorization': 'Bearer {CLERK_SESSION_TOKEN}'}
)
result = response.json()
if 'data' in result:
print(f"Total API keys: {result['total']}")
for key in result['data']:
print(f"{key['name']}: {key['keyPreview']}")
else:
print(f"Error: {result.get('error')}")
{
"data": [
{
"id": "key_abc123xyz789",
"name": "Production API Key",
"keyPrefix": "wy_live_a1b2c3d4",
"keyPreview": "wy_live_a1b2c3d4...****",
"environment": "live",
"scopes": ["farms:read", "farms:write", "crops:read", "crops:write"],
"isActive": true,
"lastUsedAt": "2025-10-29T14:30:00.000Z",
"usageCount": 1523,
"rateLimit": 5000,
"createdAt": "2025-10-01T10:00:00.000Z",
"updatedAt": "2025-10-29T14:30:00.000Z",
"expiresAt": null,
"metadata": {
"userAgent": "Mozilla/5.0..."
}
},
{
"id": "key_def456uvw012",
"name": "Development Testing",
"keyPrefix": "wy_test_e5f6g7h8",
"keyPreview": "wy_test_e5f6g7h8...****",
"environment": "test",
"scopes": ["all"],
"isActive": true,
"lastUsedAt": null,
"usageCount": 0,
"rateLimit": 1000,
"createdAt": "2025-10-15T16:20:00.000Z",
"updatedAt": "2025-10-15T16:20:00.000Z",
"expiresAt": "2025-12-31T23:59:59.000Z",
"metadata": {}
}
],
"total": 2
}
{
"data": [],
"total": 0
}
{
"error": "Unauthorized",
"message": "Authentication required"
}
{
"error": "Internal server error"
}
API Keys
List API Keys
List all active API keys for the authenticated user
GET
/
api
/
api-keys
curl -X GET https://www.wiseyield.co/api/api-keys \
-H "Authorization: Bearer {CLERK_SESSION_TOKEN}"
// Using Clerk session
const response = await fetch(
'https://www.wiseyield.co/api/api-keys',
{
method: 'GET',
headers: {
'Authorization': `Bearer ${clerkSessionToken}`
}
}
);
const { data, total } = await response.json();
console.log(`You have ${total} active API keys`);
data.forEach(key => {
console.log(`${key.name}: ${key.keyPreview}`);
});
async function listApiKeys() {
try {
const response = await fetch('/api/api-keys', {
method: 'GET',
headers: {
'Authorization': `Bearer ${clerkSessionToken}`
}
});
if (!response.ok) {
const error = await response.json();
if (response.status === 401) {
router.push('/sign-in');
return;
}
toast.error(error.error || 'Failed to load API keys');
return;
}
const { data, total } = await response.json();
return { keys: data, total };
} catch (error) {
toast.error('Network error. Please try again.');
}
}
import requests
response = requests.get(
'https://www.wiseyield.co/api/api-keys',
headers={'Authorization': 'Bearer {CLERK_SESSION_TOKEN}'}
)
result = response.json()
if 'data' in result:
print(f"Total API keys: {result['total']}")
for key in result['data']:
print(f"{key['name']}: {key['keyPreview']}")
else:
print(f"Error: {result.get('error')}")
{
"data": [
{
"id": "key_abc123xyz789",
"name": "Production API Key",
"keyPrefix": "wy_live_a1b2c3d4",
"keyPreview": "wy_live_a1b2c3d4...****",
"environment": "live",
"scopes": ["farms:read", "farms:write", "crops:read", "crops:write"],
"isActive": true,
"lastUsedAt": "2025-10-29T14:30:00.000Z",
"usageCount": 1523,
"rateLimit": 5000,
"createdAt": "2025-10-01T10:00:00.000Z",
"updatedAt": "2025-10-29T14:30:00.000Z",
"expiresAt": null,
"metadata": {
"userAgent": "Mozilla/5.0..."
}
},
{
"id": "key_def456uvw012",
"name": "Development Testing",
"keyPrefix": "wy_test_e5f6g7h8",
"keyPreview": "wy_test_e5f6g7h8...****",
"environment": "test",
"scopes": ["all"],
"isActive": true,
"lastUsedAt": null,
"usageCount": 0,
"rateLimit": 1000,
"createdAt": "2025-10-15T16:20:00.000Z",
"updatedAt": "2025-10-15T16:20:00.000Z",
"expiresAt": "2025-12-31T23:59:59.000Z",
"metadata": {}
}
],
"total": 2
}
{
"data": [],
"total": 0
}
{
"error": "Unauthorized",
"message": "Authentication required"
}
{
"error": "Internal server error"
}
Endpoint
GET https://www.wiseyield.co/api/api-keys
Dashboard-only endpoint. Authentication is via the WiseYield browser session (Clerk cookies). It cannot be called from a server-to-server integration today. Use the WiseYield dashboard’s Settings → API Keys page to manage keys; this page documents the wire format that page consumes.
Authentication
Requires a signed-in WiseYield session (Clerk cookies set by the dashboard). The cURL and Bearer examples below illustrate the wire shape but are not directly callable outside the browser —fetch('/api/api-keys', { credentials: 'include' }) from the dashboard is how it’s actually invoked.
Response
Returns list of API keys with usage statistics.array
Array of API key objects (sorted by creation date, newest first)
Show API Key Object
Show API Key Object
string
Unique identifier (format:
key_{nanoid})string
User-defined name for the API key
string
First 16 characters of the key (e.g.,
wy_live_abc123de)string
Masked version for display (e.g.,
wy_live_abc123de...****)string
Environment type:
live (production) or test (development)array
Array of permission scopes (e.g.,
["farms:read", "crops:write"])boolean
Whether the key is currently active
string
ISO 8601 timestamp of last usage (null if never used)
number
Total number of API requests made with this key
number
Maximum requests per hour (1-100,000)
string
ISO 8601 timestamp of creation
string
ISO 8601 timestamp of last update
string
ISO 8601 timestamp when key expires (null if no expiration)
object
Custom metadata attached to the key
number
Total number of active API keys
curl -X GET https://www.wiseyield.co/api/api-keys \
-H "Authorization: Bearer {CLERK_SESSION_TOKEN}"
// Using Clerk session
const response = await fetch(
'https://www.wiseyield.co/api/api-keys',
{
method: 'GET',
headers: {
'Authorization': `Bearer ${clerkSessionToken}`
}
}
);
const { data, total } = await response.json();
console.log(`You have ${total} active API keys`);
data.forEach(key => {
console.log(`${key.name}: ${key.keyPreview}`);
});
async function listApiKeys() {
try {
const response = await fetch('/api/api-keys', {
method: 'GET',
headers: {
'Authorization': `Bearer ${clerkSessionToken}`
}
});
if (!response.ok) {
const error = await response.json();
if (response.status === 401) {
router.push('/sign-in');
return;
}
toast.error(error.error || 'Failed to load API keys');
return;
}
const { data, total } = await response.json();
return { keys: data, total };
} catch (error) {
toast.error('Network error. Please try again.');
}
}
import requests
response = requests.get(
'https://www.wiseyield.co/api/api-keys',
headers={'Authorization': 'Bearer {CLERK_SESSION_TOKEN}'}
)
result = response.json()
if 'data' in result:
print(f"Total API keys: {result['total']}")
for key in result['data']:
print(f"{key['name']}: {key['keyPreview']}")
else:
print(f"Error: {result.get('error')}")
{
"data": [
{
"id": "key_abc123xyz789",
"name": "Production API Key",
"keyPrefix": "wy_live_a1b2c3d4",
"keyPreview": "wy_live_a1b2c3d4...****",
"environment": "live",
"scopes": ["farms:read", "farms:write", "crops:read", "crops:write"],
"isActive": true,
"lastUsedAt": "2025-10-29T14:30:00.000Z",
"usageCount": 1523,
"rateLimit": 5000,
"createdAt": "2025-10-01T10:00:00.000Z",
"updatedAt": "2025-10-29T14:30:00.000Z",
"expiresAt": null,
"metadata": {
"userAgent": "Mozilla/5.0..."
}
},
{
"id": "key_def456uvw012",
"name": "Development Testing",
"keyPrefix": "wy_test_e5f6g7h8",
"keyPreview": "wy_test_e5f6g7h8...****",
"environment": "test",
"scopes": ["all"],
"isActive": true,
"lastUsedAt": null,
"usageCount": 0,
"rateLimit": 1000,
"createdAt": "2025-10-15T16:20:00.000Z",
"updatedAt": "2025-10-15T16:20:00.000Z",
"expiresAt": "2025-12-31T23:59:59.000Z",
"metadata": {}
}
],
"total": 2
}
{
"data": [],
"total": 0
}
{
"error": "Unauthorized",
"message": "Authentication required"
}
{
"error": "Internal server error"
}
Errors
error
Unauthorized - User not signed in via Clerk
error
Internal Server Error - Failed to fetch API keys
How It Works
Query Flow:- Authentication: Verifies user is signed in via Clerk
- Database Query: Fetches all API keys where:
userIdmatches authenticated userrevokedAtisnull(not revoked)
- Sorting: Orders by
createdAtdescending (newest first) - Security Masking: Masks full keys using
maskApiKey()function - Response: Returns array of masked keys with metadata
API Key Structure
Key Format
wy_{environment}_{48_hex_characters}
Example (Live):
wy_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2
Example (Test):
wy_test_x9y8z7w6v5u4t3s2r1q0p9o8n7m6l5k4j3i2h1g0f9e8
- Prefix:
wy_(WiseYield identifier) - Environment:
liveortest - Random part: 48 hexadecimal characters (24 random bytes)
Environments
Available Scopes
Farm Management:farms:read- View farmsfarms:write- Create and update farms
crops:read- View cropscrops:write- Create and update crops
fields:read- View fieldsfields:write- Create and update fields
analytics:read- View analytics and predictions
tasks:read- View taskstasks:write- Create and update tasks
team:read- View team membersteam:write- Manage team members
webhooks:read- View webhookswebhooks:write- Create and update webhooks
all- Full access to all resources
Use Cases
Display API Keys Table
function ApiKeysTable() {
const [keys, setKeys] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function loadKeys() {
const response = await fetch('/api/api-keys', {
headers: { 'Authorization': `Bearer ${clerkSessionToken}` }
});
const { data } = await response.json();
setKeys(data);
setLoading(false);
}
loadKeys();
}, []);
if (loading) return <div>Loading...</div>;
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Key</th>
<th>Environment</th>
<th>Scopes</th>
<th>Usage</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{keys.map((key) => (
<tr key={key.id}>
<td>{key.name}</td>
<td>
<code className="text-sm">{key.keyPreview}</code>
</td>
<td>
<Badge color={key.environment === 'live' ? 'green' : 'blue'}>
{key.environment}
</Badge>
</td>
<td>
<div className="flex flex-wrap gap-1">
{key.scopes.map((scope) => (
<Badge key={scope} variant="outline" size="sm">
{scope}
</Badge>
))}
</div>
</td>
<td>
<div className="text-sm">
<div>{key.usageCount.toLocaleString()} requests</div>
<div className="text-gray-500">
{key.rateLimit.toLocaleString()}/hour limit
</div>
</div>
</td>
<td>
{new Date(key.createdAt).toLocaleDateString()}
</td>
<td>
<button onClick={() => revokeKey(key.id)}>
Revoke
</button>
</td>
</tr>
))}
</tbody>
</table>
);
}
Check Key Usage
async function checkKeyUsage(keyId) {
const response = await fetch('/api/api-keys');
const { data } = await response.json();
const key = data.find(k => k.id === keyId);
if (!key) {
console.log('Key not found or revoked');
return;
}
const percentUsed = (key.usageCount / key.rateLimit) * 100;
console.log(`Key: ${key.name}`);
console.log(`Usage: ${key.usageCount} / ${key.rateLimit} (${percentUsed.toFixed(1)}%)`);
console.log(`Last used: ${key.lastUsedAt || 'Never'}`);
if (percentUsed > 80) {
console.warn('⚠️ Approaching rate limit!');
}
}
Filter by Environment
async function getProductionKeys() {
const response = await fetch('/api/api-keys');
const { data } = await response.json();
const liveKeys = data.filter(key => key.environment === 'live');
const testKeys = data.filter(key => key.environment === 'test');
return { liveKeys, testKeys };
}
Check for Expiring Keys
async function checkExpiringKeys() {
const response = await fetch('/api/api-keys');
const { data } = await response.json();
const now = new Date();
const thirtyDaysFromNow = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000);
const expiringSoon = data.filter(key => {
if (!key.expiresAt) return false;
const expiresAt = new Date(key.expiresAt);
return expiresAt < thirtyDaysFromNow && expiresAt > now;
});
if (expiringSoon.length > 0) {
console.warn(`${expiringSoon.length} key(s) expiring within 30 days:`);
expiringSoon.forEach(key => {
console.log(`- ${key.name}: expires ${new Date(key.expiresAt).toLocaleDateString()}`);
});
}
}
Best Practices
Regular Audits: Review API keys list monthly to remove unused keys.
Monitor Usage: Check
usageCount and lastUsedAt to identify inactive keys.Environment Separation: Use
test keys for development, live keys only in production.Scope Principle: Grant minimum scopes needed for each use case.
Full Keys Never Shown: The complete API key is only shown once during creation. Save it securely.
Revoked Keys Excluded: This endpoint only returns active keys (revokedAt = null).
10 Key Limit: Each user can have maximum 10 active API keys. Revoke unused ones.
Security Considerations
Key Storage:- Only SHA-256 hash stored in database
- Prefix stored for identification (first 16 chars)
- Full key never retrievable after creation
- Keys masked in UI:
wy_live_abc123de...**** - Prevents accidental exposure in screenshots
- Safe to display in logs and monitoring
- Keys cannot access resources outside their scopes
allscope grants full access (use cautiously)- Scopes validated on every API request
Related Endpoints
- Create API Key - POST to generate new API key
- Revoke API Key - DELETE to revoke existing key