API Keys Management
Create and manage API keys for programmatic access to the Canary API. API keys allow external applications to send emails and access resources without user session authentication.
Authentication
All API key management endpoints require session authentication (dashboard API).
API Key Format
API keys are prefixed with cnry_ followed by a random string:
cnry_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
The full key is only shown once when created. After that, only the prefix (cnry_a1b2c3) is visible.
API Key Scopes
Restrict what actions an API key can perform:
| Scope | Description |
|---|---|
send | Send emails via POST /api/v1/send |
templates:read | Read template information |
templates:write | Create and modify templates |
logs:read | Read email logs and statistics |
If no scopes are specified, the key defaults to send only.
List API Keys
Get all API keys for the current team.
GET /api/api-keys
Required Permission
api-keys:view
Example Request
curl "https://your-domain.com/api/api-keys" \
-H "Cookie: session=..."
Response
{
"success": true,
"data": [
{
"id": "key_abc123",
"name": "Production API Key",
"keyPrefix": "cnry_a1b",
"scopes": ["send"],
"rateLimit": 1000,
"expiresAt": null,
"lastUsedAt": "2024-01-20T15:30:00.000Z",
"isActive": true,
"createdBy": "usr_xyz",
"createdAt": "2024-01-01T00:00:00.000Z"
},
{
"id": "key_def456",
"name": "Development Key",
"keyPrefix": "cnry_b2c",
"scopes": ["send", "templates:read"],
"rateLimit": 100,
"expiresAt": "2024-12-31T23:59:59.000Z",
"lastUsedAt": null,
"isActive": true,
"createdBy": "usr_xyz",
"createdAt": "2024-01-15T00:00:00.000Z"
}
]
}
Get API Key
Get a single API key by ID.
GET /api/api-keys/:id
Required Permission
api-keys:view
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | API key ID |
Example Request
curl "https://your-domain.com/api/api-keys/key_abc123" \
-H "Cookie: session=..."
Response
{
"success": true,
"data": {
"id": "key_abc123",
"name": "Production API Key",
"keyPrefix": "cnry_a1b",
"scopes": ["send"],
"rateLimit": 1000,
"expiresAt": null,
"lastUsedAt": "2024-01-20T15:30:00.000Z",
"isActive": true,
"createdBy": "usr_xyz",
"createdAt": "2024-01-01T00:00:00.000Z"
}
}
Create API Key
Create a new API key.
POST /api/api-keys
Required Permission
api-keys:create
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Key name (1-100 chars) |
scopes | string[] | No | Allowed scopes (defaults to ["send"]) |
rateLimit | number | No | Requests per minute (1-10,000) |
expiresAt | string | No | Expiration date (ISO 8601) |
Example Request
curl -X POST "https://your-domain.com/api/api-keys" \
-H "Cookie: session=..." \
-H "Content-Type: application/json" \
-d '{
"name": "Production API Key",
"scopes": ["send", "logs:read"],
"rateLimit": 1000
}'
Response
The full API key is only returned in this response. Save it securely - it cannot be retrieved later.
{
"success": true,
"data": {
"id": "key_new789",
"name": "Production API Key",
"key": "cnry_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"keyPrefix": "cnry_a1b",
"scopes": ["send", "logs:read"],
"rateLimit": 1000,
"expiresAt": null,
"isActive": true,
"createdBy": "usr_xyz",
"createdAt": "2024-01-20T10:00:00.000Z"
}
}
Save the
keyvalue immediately - it will not be shown again. If lost, you must regenerate the key.
Example with Expiration
curl -X POST "https://your-domain.com/api/api-keys" \
-H "Cookie: session=..." \
-H "Content-Type: application/json" \
-d '{
"name": "Temporary Integration Key",
"scopes": ["send"],
"rateLimit": 500,
"expiresAt": "2024-06-30T23:59:59.000Z"
}'
Update API Key
Update an existing API key.
PUT /api/api-keys/:id
Required Permission
api-keys:create
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | API key ID |
Request Body
All fields are optional. Only provided fields will be updated.
| Field | Type | Description |
|---|---|---|
name | string | Key name (1-100 chars) |
scopes | string[] | Allowed scopes |
rateLimit | number | Requests per minute (1-10,000) |
isActive | boolean | Enable/disable the key |
Example Request
curl -X PUT "https://your-domain.com/api/api-keys/key_abc123" \
-H "Cookie: session=..." \
-H "Content-Type: application/json" \
-d '{
"name": "Production API Key (Updated)",
"rateLimit": 2000,
"isActive": true
}'
Response
{
"success": true,
"data": {
"id": "key_abc123",
"name": "Production API Key (Updated)",
"keyPrefix": "cnry_a1b",
"scopes": ["send"],
"rateLimit": 2000,
"isActive": true,
"updatedAt": "2024-01-20T15:00:00.000Z"
}
}
Delete API Key
Permanently delete an API key.
DELETE /api/api-keys/:id
Required Permission
api-keys:delete
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | API key ID |
Example Request
curl -X DELETE "https://your-domain.com/api/api-keys/key_abc123" \
-H "Cookie: session=..."
Response
{
"success": true
}
Regenerate API Key
Generate a new key value for an existing API key. The old key immediately stops working.
POST /api/api-keys/:id/regenerate
Required Permission
api-keys:create
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | API key ID |
Example Request
curl -X POST "https://your-domain.com/api/api-keys/key_abc123/regenerate" \
-H "Cookie: session=..."
Response
The new key is only returned in this response.
{
"success": true,
"data": {
"id": "key_abc123",
"name": "Production API Key",
"key": "cnry_x9y8z7w6v5u4t3s2r1q0p9o8n7m6l5k4",
"keyPrefix": "cnry_x9y",
"scopes": ["send"],
"rateLimit": 1000,
"isActive": true
}
}
The previous key is invalidated immediately. Update all applications using this key.
Using API Keys
Include the API key in the X-API-Key header:
curl -X POST "https://your-domain.com/api/v1/send" \
-H "X-API-Key: cnry_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
-H "Content-Type: application/json" \
-d '{
"templateId": "welcome",
"to": "user@example.com"
}'
Rate Limiting
When the rate limit is exceeded, the API returns:
{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Try again later."
}
}
HTTP Status: 429 Too Many Requests
Expired Keys
When using an expired key:
{
"success": false,
"error": {
"code": "TOKEN_EXPIRED",
"message": "API key has expired"
}
}
HTTP Status: 401 Unauthorized
Best Practices
- Use separate keys - Create different keys for development, staging, and production
- Limit scopes - Only grant the scopes each integration actually needs
- Set rate limits - Protect against runaway scripts or abuse
- Use expiration - Set expiration dates for temporary integrations
- Rotate regularly - Regenerate keys periodically for security
- Monitor usage - Check
lastUsedAtto identify unused keys - Secure storage - Store keys in environment variables or secret managers, never in code