KeyPackage API
Overview
Section titled “Overview”KeyPackages are one-time-use cryptographic bundles that enable adding devices to MLS groups. Each device uploads KeyPackages to a server, where they can be fetched when adding that device to a group.
KeyPackage Lifecycle:
- Device generates KeyPackages using its Device Identity keypair
- Device uploads KeyPackages to a server
- Server stores KeyPackages indexed by device_id
- Other devices fetch KeyPackages when inviting this device to groups
- After use, KeyPackage is deleted (one-time use only)
Upload KeyPackages
Section titled “Upload KeyPackages”Upload a batch of MLS KeyPackages for this device.
Endpoint
Section titled “Endpoint”POST /v1/keypackages/uploadRequest
Section titled “Request”POST /v1/keypackages/upload HTTP/1.1Authorization: Bearer {access_token}Content-Type: application/json{ "device_id": "8f6b753d772275127557397be1edce476cd698ed5f09a2b4a70fb64a0577ab2a", "keypackages": [ "base64_mls_keypackage_1", "base64_mls_keypackage_2", "base64_mls_keypackage_3" ]}Parameters:
device_id: Blake3 hash of device public key (64 hex chars)keypackages: Array of base64-encoded MLS KeyPackages (max 100 per request)
Validation:
- KeyPackages must be valid MLS format (server validates structure, not signature)
- Each KeyPackage must be unique (no duplicates)
- Device must be announced (have valid delivery address)
Response (200 OK)
Section titled “Response (200 OK)”{ "uploaded": 3, "total_available": 23, "expires_at": 1763568947}Response fields:
uploaded: Number of KeyPackages successfully uploaded in this requesttotal_available: Total KeyPackages now available for this deviceexpires_at: Unix timestamp when KeyPackages expire (typically 30 days)
Recommended practice: Keep 50-100 KeyPackages available at all times. Upload more when count drops below 20.
Error Responses
Section titled “Error Responses”400 Bad Request
Section titled “400 Bad Request”{ "error": "INVALID_KEYPACKAGE", "message": "KeyPackage format invalid or corrupted", "code": 4015}413 Payload Too Large
Section titled “413 Payload Too Large”{ "error": "TOO_MANY_KEYPACKAGES", "message": "Maximum 100 KeyPackages per upload request", "code": 4003}Fetch KeyPackages
Section titled “Fetch KeyPackages”Retrieve available KeyPackages for a specific device. Used when inviting that device to a group.
Endpoint
Section titled “Endpoint”GET /v1/keypackages/{device_id}Request
Section titled “Request”GET /v1/keypackages/8f6b753d772275127557397be1edce476cd698ed5f09a2b4a70fb64a0577ab2a?count=1 HTTP/1.1Authorization: Bearer {access_token}Query Parameters:
count: Number of KeyPackages to fetch (default: 1, max: 10)
Authentication required: Must have valid device announcement.
Response (200 OK)
Section titled “Response (200 OK)”{ "device_id": "8f6b753d772275127557397be1edce476cd698ed5f09a2b4a70fb64a0577ab2a", "keypackages": [ "base64_mls_keypackage_1" ], "remaining": 22, "fetched_at": 1763569257}Response fields:
device_id: Device these KeyPackages belong tokeypackages: Array of base64-encoded MLS KeyPackagesremaining: KeyPackages still available for this devicefetched_at: Unix timestamp of fetch
Consumption: Fetched KeyPackages are immediately marked as “consumed” and won’t be returned again (one-time use).
Error Responses
Section titled “Error Responses”404 Not Found
Section titled “404 Not Found”{ "error": "DEVICE_NOT_FOUND", "message": "Device not registered or has no KeyPackages", "code": 4014}410 Gone
Section titled “410 Gone”{ "error": "INVALID_KEYPACKAGE", "message": "Device has exhausted all KeyPackages. Ask device owner to upload more.", "code": 4015}What to do: Wait for device owner to come online and upload more KeyPackages, or show error to user.
Fetch KeyPackages for Multiple Devices
Section titled “Fetch KeyPackages for Multiple Devices”Batch fetch KeyPackages for multiple devices at once (for multi-device invitations).
Endpoint
Section titled “Endpoint”POST /v1/keypackages/batch-fetchRequest
Section titled “Request”POST /v1/keypackages/batch-fetch HTTP/1.1Authorization: Bearer {access_token}Content-Type: application/json{ "devices": [ { "device_id": "8f6b753d772275127557397be1edce476cd698ed5f09a2b4a70fb64a0577ab2a", "count": 1 }, { "device_id": "3e77f82208e44f22d6d0c7b46c435a063f97f5e9ca44947f79555c01925a1c2e", "count": 1 } ]}Response (200 OK)
Section titled “Response (200 OK)”{ "results": [ { "device_id": "8f6b753d772275127557397be1edce476cd698ed5f09a2b4a70fb64a0577ab2a", "keypackages": ["base64_keypackage"], "success": true }, { "device_id": "3e77f82208e44f22d6d0c7b46c435a063f97f5e9ca44947f79555c01925a1c2e", "keypackages": [], "success": false, "error": "NO_KEYPACKAGES_AVAILABLE" } ], "successful": 1, "failed": 1}Use case: Inviting a user with multiple devices to a group in a single MLS commit.
Delete All KeyPackages
Section titled “Delete All KeyPackages”Remove all KeyPackages for this device (used when rotating device keys or deregistering).
Endpoint
Section titled “Endpoint”DELETE /v1/keypackagesRequest
Section titled “Request”DELETE /v1/keypackages HTTP/1.1Authorization: Bearer {access_token}Response (200 OK)
Section titled “Response (200 OK)”{ "deleted": 22, "device_id": "8f6b753d772275127557397be1edce476cd698ed5f09a2b4a70fb64a0577ab2a"}Important Notes
Section titled “Important Notes”KeyPackage Lifecycle
Section titled “KeyPackage Lifecycle”Generation:
let keypackage_bundle = device.generate_key_packages(100)?;
// keypackage_bundle contains:// - public KeyPackages (upload to server)// - private keys (store locally for welcome decryption)Upload:
- Generate 50-100 KeyPackages at device registration
- Upload to server immediately
- Monitor count and upload more when <20 remaining
Consumption:
- Each KeyPackage used once (for adding device to one group)
- After use, server deletes it (prevents replay)
- Device uploads more as needed
Expiration:
- KeyPackages expire after 30 days (unused)
- Server automatically deletes expired KeyPackages
- Device should refresh before expiration
Cross-Server KeyPackage Fetching
Section titled “Cross-Server KeyPackage Fetching”Scenario: Alice@server1.com wants to invite Bob@server2.com to a group.
Flow:
- Alice fetches Bob’s InfoPackage (contains device_id +
keypackage_serverfield) - Alice’s server contacts Bob’s
keypackage_servervia federation - Bob’s server returns KeyPackages for Bob’s devices
- Alice creates MLS commit with fetched KeyPackages
Federation endpoint: GET /federation/v1/keypackages/{device_id}
Security Considerations
Section titled “Security Considerations”Server Validation:
- Server validates KeyPackage structure (MLS format)
- Server does NOT verify signatures (recipients do this)
- Server does NOT decrypt KeyPackages (opaque blobs)
Privacy:
- KeyPackages contain device_id (public)
- KeyPackages contain ciphersuite info (public)
- KeyPackages do NOT contain user identity info
- Fetching is authenticated (prevents scraping)
Rate Limiting
Section titled “Rate Limiting”Upload limits:
- Max 100 KeyPackages per request
- Max 500 KeyPackages stored per device
- Max 10 upload requests per hour
Fetch limits:
- Max 10 KeyPackages per fetch
- Max 100 fetches per hour per device