Version: 1.0 | Last Updated: April 2026 | Status: Draft
BYOK API Reference
Complete API reference for ASCEND BYOK/CMK encryption endpoints.
Authentication
All BYOK endpoints require a valid Bearer token. The authenticated user's ID (from the JWT sub claim) is used as the rate limit key.
Authorization: Bearer <your_jwt_token>
Base URL
https://pilot.owkai.app/api/v1/byok
Register Encryption Key
Register your AWS KMS Customer Managed Key with ASCEND.
POST /api/v1/byok/keys
Request Body
| Field | Type | Required | Description | Validation |
|---|---|---|---|---|
cmk_arn | string | Yes | Full ARN of your AWS KMS key | Must match arn:aws:kms:<region>:<account>:key/<key-id> |
cmk_alias | string | No | Friendly alias for the key | None |
Example Request
curl -X POST https://pilot.owkai.app/api/v1/byok/keys \
-H "Authorization: Bearer $ASCEND_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"cmk_arn": "arn:aws:kms:us-east-2:<YOUR_AWS_ACCOUNT_ID>:key/12345678-1234-1234-1234-123456789012",
"cmk_alias": "ascend-production-key"
}'
Response (201 Created)
Returns KeyStatusResponse:
{
"organization_id": 4,
"cmk_arn": "arn:aws:kms:us-east-2:<YOUR_AWS_ACCOUNT_ID>:key/12345678-1234-1234-1234-123456789012",
"cmk_alias": "ascend-production-key",
"status": "active",
"status_reason": "Key validated successfully",
"last_validated_at": "2026-04-02T10:30:00Z",
"last_rotation_at": null,
"created_at": "2026-04-02T10:30:00Z"
}
Response Fields
| Field | Type | Description |
|---|---|---|
organization_id | int | Organization that owns this key |
cmk_arn | string | Full ARN of the registered CMK |
cmk_alias | string or null | Friendly alias |
status | string | Key status: active, pending_waiver, disabled, revoked |
status_reason | string or null | Human-readable status explanation |
last_validated_at | datetime or null | Last successful CMK access validation |
last_rotation_at | datetime or null | Last DEK rotation timestamp |
created_at | datetime | Registration timestamp |
Errors
| Error Code | HTTP Status | When |
|---|---|---|
BYOK_001 | 400 | cmk_arn does not match expected ARN format |
BYOK_002 | 403 | ASCEND cannot access the CMK (check key policy) |
BYOK_003 | 409 | Organization already has a registered key |
Get Key Status
Get the current status of your registered encryption key.
GET /api/v1/byok/keys
Example Request
curl https://pilot.owkai.app/api/v1/byok/keys \
-H "Authorization: Bearer $ASCEND_TOKEN"
Response (200 OK)
Returns KeyStatusResponse if BYOK is configured, or null if no key is registered.
{
"organization_id": 4,
"cmk_arn": "arn:aws:kms:us-east-2:<YOUR_AWS_ACCOUNT_ID>:key/12345678-1234-1234-1234-123456789012",
"cmk_alias": "ascend-production-key",
"status": "active",
"status_reason": "Key validated successfully",
"last_validated_at": "2026-04-02T11:00:00Z",
"last_rotation_at": "2026-04-02T10:45:00Z",
"created_at": "2026-04-02T10:30:00Z"
}
See Register Encryption Key for field descriptions.
Revoke Encryption Key
Remove your BYOK key. The key record is marked as revoked (not deleted) to preserve the audit trail.
DELETE /api/v1/byok/keys
Example Request
curl -X DELETE https://pilot.owkai.app/api/v1/byok/keys \
-H "Authorization: Bearer $ASCEND_TOKEN"
Response (200 OK)
{
"status": "revoked",
"message": "Encryption key has been revoked"
}
Errors
| Error Code | HTTP Status | When |
|---|---|---|
BYOK_005 | 404 | No key registered for this organization |
Rotate Data Encryption Key
Trigger rotation of the Data Encryption Key (DEK) wrapped by your CMK. This generates a new DEK; old DEKs are kept for decrypting existing data. This does not rotate the CMK itself.
POST /api/v1/byok/keys/rotate
This endpoint takes no request body.
Example Request
curl -X POST https://pilot.owkai.app/api/v1/byok/keys/rotate \
-H "Authorization: Bearer $ASCEND_TOKEN"
Response (200 OK)
Returns KeyRotationResponse:
{
"success": true,
"message": "DEK rotated to version 3",
"new_dek_version": 3,
"rotated_at": "2026-04-02T10:45:00Z"
}
Response Fields
| Field | Type | Description |
|---|---|---|
success | bool | Whether rotation completed |
message | string | Human-readable result |
new_dek_version | int or null | New DEK version number |
rotated_at | datetime | Rotation timestamp |
Errors
| Error Code | HTTP Status | When |
|---|---|---|
BYOK_002 | 403 | Key status is not active (cannot rotate) |
BYOK_005 | 404 | No key registered for this organization |
BYOK_006 | 503 | Cannot access CMK for rotation |
Health Check
Check the health status of your BYOK configuration.
GET /api/v1/byok/health
Example Request
curl https://pilot.owkai.app/api/v1/byok/health \
-H "Authorization: Bearer $ASCEND_TOKEN"
Response (200 OK) — BYOK Enabled
Returns KeyHealthResponse:
{
"byok_enabled": true,
"status": "active",
"cmk_accessible": true,
"last_validated_at": "2026-04-02T11:00:00Z",
"cmk_arn_prefix": "arn:aws:kms:us-east-2:<YOUR_AWS_AC...",
"dek_version": 3
}
Response (200 OK) — BYOK Not Configured
{
"byok_enabled": false,
"status": null,
"cmk_accessible": null,
"last_validated_at": null,
"cmk_arn_prefix": null,
"dek_version": null
}
Response Fields
| Field | Type | Description |
|---|---|---|
byok_enabled | bool | Whether BYOK is configured for this organization |
status | string or null | Key status from database |
cmk_accessible | bool or null | Whether ASCEND can currently access the CMK |
last_validated_at | datetime or null | Last successful validation timestamp |
cmk_arn_prefix | string or null | First 40 characters of CMK ARN (masked for security) |
dek_version | int or null | Current DEK version number |
Get Audit Log
Retrieve BYOK operation audit log.
GET /api/v1/byok/audit
Query Parameters
| Parameter | Type | Default | Max | Description |
|---|---|---|---|---|
limit | int | 50 | 500 | Number of entries to return |
offset | int | 0 | — | Pagination offset |
Example Request
curl "https://pilot.owkai.app/api/v1/byok/audit?limit=50&offset=0" \
-H "Authorization: Bearer $ASCEND_TOKEN"
Response (200 OK)
Returns BYOKAuditResponse:
{
"entries": [
{
"id": 42,
"operation": "key_rotated",
"success": true,
"error_message": null,
"created_at": "2026-04-02T10:45:00Z"
},
{
"id": 41,
"operation": "health_check",
"success": true,
"error_message": null,
"created_at": "2026-04-02T10:30:00Z"
}
],
"total_count": 42
}
Audit Entry Fields
| Field | Type | Description |
|---|---|---|
id | int | Unique audit entry ID |
operation | string | Operation type (see below) |
success | bool | Whether the operation succeeded |
error_message | string or null | Error detail if operation failed |
created_at | datetime | When the operation occurred |
Operation Types
| Operation | Description |
|---|---|
key_registered | CMK registration |
key_revoked | CMK revocation |
key_rotated | DEK rotation |
health_check | Health check performed |
legal_waiver_acknowledged | Legal waiver acknowledged |
Pagination
| Field | Type | Description |
|---|---|---|
entries | array | Audit entries for this page |
total_count | int | Total entries across all pages |
Get Legal Waiver
Retrieve the legal waiver text that must be acknowledged before BYOK activation.
GET /api/v1/byok/legal-waiver
Example Request
curl https://pilot.owkai.app/api/v1/byok/legal-waiver \
-H "Authorization: Bearer $ASCEND_TOKEN"
Response (200 OK)
{
"waiver_text": "## BYOK Data Sovereignty Acknowledgment\n\nBy enabling Bring Your Own Key (BYOK) encryption, you acknowledge:\n\n1. **Key Control**: You have sole control over your AWS KMS Customer Managed Key (CMK).\n\n2. **Permanent Data Loss Risk**: If you delete your CMK or revoke ASCEND's access without first disabling BYOK and exporting your data, YOUR DATA WILL BE PERMANENTLY UNRECOVERABLE...",
"required_acknowledgments": [
{
"field": "acknowledge_data_loss_risk",
"description": "I acknowledge the permanent data loss risk"
},
{
"field": "acknowledge_key_management_responsibility",
"description": "I accept responsibility for CMK management"
},
{
"field": "acknowledge_no_liability",
"description": "I understand OW-KAI is not liable for key mismanagement"
}
]
}
Response Fields
| Field | Type | Description |
|---|---|---|
waiver_text | string | Full waiver text in Markdown format |
required_acknowledgments | array | List of fields that must be set to true in the acknowledge request |
required_acknowledgments[].field | string | Field name for the POST request body |
required_acknowledgments[].description | string | Human-readable description of the acknowledgment |
Acknowledge Legal Waiver
Acknowledge the legal waiver to activate BYOK encryption. All three acknowledgments must be true.
POST /api/v1/byok/legal-waiver
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
acknowledge_data_loss_risk | bool | Yes | Must be true |
acknowledge_key_management_responsibility | bool | Yes | Must be true |
acknowledge_no_liability | bool | Yes | Must be true |
Example Request
curl -X POST https://pilot.owkai.app/api/v1/byok/legal-waiver \
-H "Authorization: Bearer $ASCEND_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"acknowledge_data_loss_risk": true,
"acknowledge_key_management_responsibility": true,
"acknowledge_no_liability": true
}'
Response (200 OK)
Returns LegalAcknowledgmentResponse:
{
"acknowledged": true,
"acknowledged_at": "2026-04-02T10:35:00Z",
"acknowledged_by_user_id": 15,
"message": "Legal waiver acknowledged. BYOK encryption is now fully enabled."
}
Response Fields
| Field | Type | Description |
|---|---|---|
acknowledged | bool | Always true on success |
acknowledged_at | datetime | When the waiver was acknowledged |
acknowledged_by_user_id | int | User ID of the person who acknowledged |
message | string | Confirmation message |
Errors
| Error Code | HTTP Status | When |
|---|---|---|
BYOK_004 | 400 | Not all acknowledgments were set to true |
BYOK_005 | 404 | No BYOK key registered (register a key first) |
Get Waiver Status
Check if the legal waiver has been acknowledged.
GET /api/v1/byok/legal-waiver/status
Example Request
curl https://pilot.owkai.app/api/v1/byok/legal-waiver/status \
-H "Authorization: Bearer $ASCEND_TOKEN"
Response (200 OK) — Acknowledged
{
"byok_configured": true,
"waiver_acknowledged": true,
"acknowledged_at": "2026-04-02T10:35:00Z",
"acknowledged_by_user_id": 15
}
Response (200 OK) — No BYOK Key
{
"byok_configured": false,
"waiver_acknowledged": false,
"acknowledged_at": null,
"acknowledged_by_user_id": null
}
Response Fields
| Field | Type | Description |
|---|---|---|
byok_configured | bool | Whether a BYOK key is registered |
waiver_acknowledged | bool | Whether the legal waiver has been acknowledged |
acknowledged_at | datetime or null | When the waiver was acknowledged |
acknowledged_by_user_id | int or null | User ID of acknowledger |
Error Codes
All BYOK errors return a structured JSON response:
{
"error_code": "BYOK_002",
"message": "KMS key access denied",
"detail": "Unable to access the provided KMS key: AccessDeniedException. Please verify the key policy grants ASCEND access.",
"timestamp": "2026-04-02T10:30:00Z",
"request_id": "req-abc-123"
}
| Field | Type | Description |
|---|---|---|
error_code | string | Unique BYOK error identifier |
message | string | Short error description |
detail | string | Specific context for this occurrence |
timestamp | string | ISO 8601 UTC timestamp |
request_id | string or null | Value of X-Request-ID header if provided |
Error Code Registry
| Code | HTTP Status | Message | When It Occurs |
|---|---|---|---|
BYOK_001 | 400 | Invalid KMS ARN format | cmk_arn fails regex validation |
BYOK_002 | 403 | KMS key access denied | ASCEND cannot access CMK, or key status is not active |
BYOK_003 | 409 | Key already registered | Organization already has a registered key |
BYOK_004 | 400 | Legal waiver not acknowledged | Not all 3 acknowledgment fields set to true |
BYOK_005 | 404 | No key registered | No encryption key registered for this organization |
BYOK_006 | 503 | KMS service unavailable | CMK inaccessible during rotation or health check |
BYOK_007 | 500 | Encryption operation failed | Data encryption using CMK/DEK failed |
BYOK_008 | 500 | Decryption operation failed | Data decryption using CMK/DEK failed |
Rate Limiting
All BYOK endpoints enforce per-user rate limits. Limits are keyed on the authenticated user's ID (extracted from the JWT sub claim). Rate limit state is stored in Redis with in-memory fallback if Redis is unavailable.
Limits by Endpoint
| Endpoint | Method | Default Limit | Environment Variable |
|---|---|---|---|
/keys | POST | 5/hour | BYOK_RATE_LIMIT_REGISTER |
/keys | GET | 100/minute | BYOK_RATE_LIMIT_GET |
/keys | DELETE | 1/hour | BYOK_RATE_LIMIT_DELETE |
/keys/rotate | POST | 10/hour | BYOK_RATE_LIMIT_ROTATE |
/health | GET | 60/minute | BYOK_RATE_LIMIT_HEALTH |
/audit | GET | 60/minute | BYOK_RATE_LIMIT_AUDIT |
/legal-waiver | GET | 60/minute | BYOK_RATE_LIMIT_WAIVER_GET |
/legal-waiver | POST | 10/hour | BYOK_RATE_LIMIT_WAIVER_POST |
/legal-waiver/status | GET | 60/minute | BYOK_RATE_LIMIT_WAIVER_STATUS |
Rate Limit Exceeded Response (429)
{
"detail": "Too many requests. Please try again later.",
"retry_after": "60 seconds"
}
The response includes a Retry-After HTTP header with the number of seconds to wait.
Rate Limit Headers
All responses include standard rate limit headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Retry-After | Seconds to wait (only on 429 responses) |
Next Steps
- Setup Guide -- Step-by-step setup instructions
- Troubleshooting -- Common issues and solutions
- BYOK Overview -- Return to overview