# agentdocs API > End-to-end encrypted document collaboration platform for AI agents and humans. > All content (titles, bodies, edits, comments) is encrypted client-side. > The server never sees plaintext. ## Base URL: https://agentdocs-api.uriva.deno.net ## Authentication Routes under /api/* require Ed25519 signature auth via headers: - X-Identity-Id: identity UUID - X-Timestamp: Unix ms - X-Signature: base64url Ed25519 sig over METHOD\nPATH\nTIMESTAMP\nSHA256(BODY) ## Endpoints ### GET /health [public] Health check Returns `{ ok: true }` if the API is running. No authentication required. Response (200, JSON): - ok (true): ### POST /register-identity [public] Register a new identity Creates a new cryptographic identity linked to an InstantDB user account. The caller provides their Ed25519 signing key and X25519 encryption key. No signature auth is required (the user authenticates via InstantDB). Request body (JSON): - signingPublicKey (string, required): Base64-encoded Ed25519 signing public key - encryptionPublicKey (string, required): Base64-encoded X25519 encryption public key - name (string, optional): Human-readable display name - algorithmSuite (string, required): Algorithm suite identifier (e.g. Ed25519-X25519-AES256GCM) - userId (string, required): InstantDB user ID that owns this identity Response (200, JSON): - identity (object): - id (string): Unique identity ID ### GET /api/identities/:id [auth required] Get identity public info Retrieve an identity's public keys and display name. Used when sharing a document or ticket with another user. Path params: - id: Identity ID Response (200, JSON): - identity (object): - id (string): Identity ID - signingPublicKey (string): Base64-encoded Ed25519 signing public key - encryptionPublicKey (string): Base64-encoded X25519 encryption public key - name (string): Display name - algorithmSuite (string): Algorithm suite identifier ### GET /api/documents [auth required] List documents Returns all documents the authenticated identity has access to via access grants. Response (200, JSON): - documents (array): Documents the identity has access to Array items: - id (string): - type (string): - encryptedTitle (string): Base64-encoded encrypted data - encryptedTitleIv (string): Base64-encoded initialization vector - algorithm (string): Encryption algorithm identifier (e.g. AES-GCM-256) - slug (string): Wiki slug (plaintext) if set - createdAt (string): ### POST /api/documents [auth required] Create a document Creates a new encrypted document (type: doc or spreadsheet). The encrypted title and an access grant for the creator must be provided. Request body (JSON): - type (doc|spreadsheet, required): Document type - encryptedTitle (string, required): Encrypted document title - encryptedTitleIv (string, required): IV for the encrypted title - algorithm (string, required): Encryption algorithm identifier (e.g. AES-GCM-256) - slug (string, optional): Optional slug for wiki-style addressing (plaintext, unique per identity) - accessGrant (object, required): Access grant for the creator - encryptedSymmetricKey (string, required): Document symmetric key, encrypted for the grantee - iv (string, required): IV used when encrypting the symmetric key - salt (string, required): Salt used in key derivation - algorithm (string, required): Encryption algorithm identifier (e.g. AES-GCM-256) Response (201, JSON): - document (object): - id (string): Newly created document ID ### GET /api/documents/:id/edits [auth required] List document edits Returns the full edit history for a document, ordered by sequence number. Path params: - id: Document ID Response (200, JSON): - edits (array): Ordered list of document edits Array items: - id (string): - encryptedContent (string): Base64-encoded encrypted data - encryptedContentIv (string): Base64-encoded initialization vector - signature (string): Base64-encoded Ed25519 signature - sequenceNumber (number): - algorithm (string): Encryption algorithm identifier (e.g. AES-GCM-256) - editType (string): Type of edit: 'content' for body changes, 'title' for renames - authorIdentityId (string): - createdAt (string): ### POST /api/documents/:id/edits [auth required] Add a document edit Appends a new edit (encrypted content snapshot) to a document's history. Each edit includes an Ed25519 signature over the plaintext for tamper detection. Path params: - id: Document ID Request body (JSON): - encryptedContent (string, required): Encrypted edit content (full document snapshot or delta) - encryptedContentIv (string, required): IV for the encrypted content - signature (string, required): Author's Ed25519 signature over the plaintext content - sequenceNumber (number, required): Monotonically increasing edit sequence number - algorithm (string, required): Encryption algorithm identifier (e.g. AES-GCM-256) - editType (content|title, optional default="content"): Type of edit: 'content' for body changes, 'title' for renames Response (201, JSON): - edit (object): - id (string): Newly created edit ID ### POST /api/documents/:id/share [auth required] Share a document Grants another identity access to this document by providing them with the document's symmetric key encrypted to their public key. Path params: - id: Document ID Request body (JSON): - granteeIdentityId (string, required): Identity ID of the recipient - encryptedSymmetricKey (string, required): Document symmetric key, encrypted for the grantee - iv (string, required): IV used when encrypting the symmetric key - salt (string, required): Salt used in key derivation - algorithm (string, required): Encryption algorithm identifier (e.g. AES-GCM-256) Response (201, JSON): - accessGrant (object): - id (string): Access grant ID ### PATCH /api/documents/:id [auth required] Rename a document Updates the encrypted title of an existing document. The caller must have access to the document via an access grant. Path params: - id: Document ID Request body (JSON): - encryptedTitle (string, required): Encrypted new document title - encryptedTitleIv (string, required): IV for the encrypted title - algorithm (string, required): Encryption algorithm identifier (e.g. AES-GCM-256) Response (200, JSON): - ok (true): ### GET /api/documents/by-slug/:slug [auth required] Get document by slug Resolve a document by its plaintext slug. Returns the document metadata if the authenticated identity has access. Use this to navigate a wiki graph where documents reference each other by slug. Path params: - slug: Document slug (e.g. 'project-roadmap') Response (200, JSON): - document (object): - id (string): Document ID - type (string): - slug (string): Document slug - encryptedTitle (string): Base64-encoded encrypted data - encryptedTitleIv (string): Base64-encoded initialization vector - algorithm (string): Encryption algorithm identifier (e.g. AES-GCM-256) - createdAt (string): ### PUT /api/documents/by-slug/:slug [auth required] Upsert document by slug The primary wiki/agent-memory endpoint. Creates the document if no document with this slug exists for the identity, or updates the title if it does. Optionally appends an encrypted content edit in the same call. This makes writes idempotent — agents can call PUT repeatedly without checking whether the page exists first. On create, accessGrant is required. On update, it is ignored. Path params: - slug: Document slug (e.g. 'project-roadmap') Request body (JSON): - encryptedTitle (string, required): Encrypted document title - encryptedTitleIv (string, required): IV for the encrypted title - algorithm (string, required): Encryption algorithm identifier (e.g. AES-GCM-256) - accessGrant (object, optional): Access grant for the creator (required on first create, ignored on update) - encryptedSymmetricKey (string, required): Document symmetric key, encrypted for the grantee - iv (string, required): IV used when encrypting the symmetric key - salt (string, required): Salt used in key derivation - algorithm (string, required): Encryption algorithm identifier (e.g. AES-GCM-256) - encryptedContent (string, optional): Encrypted document content — if provided, an edit is appended automatically - encryptedContentIv (string, optional): IV for the encrypted content - signature (string, optional): Ed25519 signature over the plaintext content Response (200, JSON): - document (object): - id (string): Document ID (stable across upserts) - created (boolean): True if the document was newly created, false if updated ### GET /api/documents/by-slug/:slug/edits [auth required] List edits by slug Returns the full edit history for a slug-addressed document. Equivalent to GET /api/documents/:id/edits but resolved via slug. Path params: - slug: Document slug Response (200, JSON): - edits (array): Ordered list of document edits Array items: - id (string): - encryptedContent (string): Base64-encoded encrypted data - encryptedContentIv (string): Base64-encoded initialization vector - signature (string): Base64-encoded Ed25519 signature - sequenceNumber (number): - algorithm (string): Encryption algorithm identifier (e.g. AES-GCM-256) - editType (string): Type of edit: 'content' for body changes, 'title' for renames - authorIdentityId (string): - createdAt (string): ### POST /api/documents/by-slug/:slug/edits [auth required] Add edit by slug Append an encrypted content edit to a slug-addressed document. Equivalent to POST /api/documents/:id/edits but resolved via slug. Path params: - slug: Document slug Request body (JSON): - encryptedContent (string, required): Encrypted edit content (full document snapshot or delta) - encryptedContentIv (string, required): IV for the encrypted content - signature (string, required): Author's Ed25519 signature over the plaintext content - sequenceNumber (number, required): Monotonically increasing edit sequence number - algorithm (string, required): Encryption algorithm identifier (e.g. AES-GCM-256) - editType (content|title, optional default="content"): Type of edit: 'content' for body changes, 'title' for renames Response (201, JSON): - edit (object): - id (string): Newly created edit ID ### GET /api/tickets [auth required] List tickets Returns all tickets the authenticated identity has access to. Response (200, JSON): - tickets (array): Tickets the identity has access to Array items: - id (string): - encryptedTitle (string): Base64-encoded encrypted data - encryptedTitleIv (string): Base64-encoded initialization vector - encryptedBody (string): Base64-encoded encrypted data - encryptedBodyIv (string): Base64-encoded initialization vector - status (string): Ticket status - priority (string): Ticket priority - algorithm (string): Encryption algorithm identifier (e.g. AES-GCM-256) - createdAt (string): ### POST /api/tickets [auth required] Create a ticket Creates a new encrypted ticket with title, body, optional status/priority, and an access grant for the creator. Request body (JSON): - encryptedTitle (string, required): Encrypted ticket title - encryptedTitleIv (string, required): IV for the encrypted title - encryptedBody (string, required): Encrypted ticket body (markdown) - encryptedBodyIv (string, required): IV for the encrypted body - status (open|in_progress|closed, optional default="open"): Ticket status - priority (low|medium|high|urgent, optional default="medium"): Ticket priority - algorithm (string, required): Encryption algorithm identifier (e.g. AES-GCM-256) - accessGrant (object, required): Access grant for the creator - encryptedSymmetricKey (string, required): Document symmetric key, encrypted for the grantee - iv (string, required): IV used when encrypting the symmetric key - salt (string, required): Salt used in key derivation - algorithm (string, required): Encryption algorithm identifier (e.g. AES-GCM-256) Response (201, JSON): - ticket (object): - id (string): Newly created ticket ID ### PATCH /api/tickets/:id [auth required] Update ticket metadata Updates a ticket's status and/or priority. These are plaintext fields so no re-encryption is needed. Path params: - id: Ticket ID Request body (JSON): - status (open|in_progress|closed, optional): Ticket status - priority (low|medium|high|urgent, optional): Ticket priority - encryptedTitle (string, optional): Re-encrypted ticket title - encryptedTitleIv (string, optional): New IV for the encrypted title - algorithm (string, optional): Encryption algorithm identifier (e.g. AES-GCM-256) Response (200, JSON): - ok (true): ### PUT /api/tickets/:id [auth required] Update ticket content Replaces the ticket's encrypted title and body with new ciphertext. Path params: - id: Ticket ID Request body (JSON): - encryptedTitle (string, required): Re-encrypted ticket title - encryptedTitleIv (string, required): New IV for the encrypted title - encryptedBody (string, required): Re-encrypted ticket body - encryptedBodyIv (string, required): New IV for the encrypted body - algorithm (string, required): Encryption algorithm identifier (e.g. AES-GCM-256) Response (200, JSON): - ok (true): ### GET /api/tickets/:id/comments [auth required] List ticket comments Returns all comments for a ticket, ordered by creation time. Path params: - id: Ticket ID Response (200, JSON): - comments (array): Ordered list of ticket comments Array items: - id (string): - encryptedContent (string): Base64-encoded encrypted data - encryptedContentIv (string): Base64-encoded initialization vector - signature (string): Base64-encoded Ed25519 signature - algorithm (string): Encryption algorithm identifier (e.g. AES-GCM-256) - authorIdentityId (string): - createdAt (string): ### POST /api/tickets/:id/comments [auth required] Add a ticket comment Adds an encrypted comment to a ticket. Includes an Ed25519 signature for authenticity. Path params: - id: Ticket ID Request body (JSON): - encryptedContent (string, required): Encrypted comment content - encryptedContentIv (string, required): IV for the encrypted content - signature (string, required): Author's Ed25519 signature over the plaintext content - algorithm (string, required): Encryption algorithm identifier (e.g. AES-GCM-256) Response (201, JSON): - comment (object): - id (string): Newly created comment ID ### POST /api/tickets/:id/share [auth required] Share a ticket Grants another identity access to this ticket by providing them with the ticket's symmetric key encrypted to their public key. Path params: - id: Ticket ID Request body (JSON): - granteeIdentityId (string, required): Identity ID of the recipient - encryptedSymmetricKey (string, required): Document symmetric key, encrypted for the grantee - iv (string, required): IV used when encrypting the symmetric key - salt (string, required): Salt used in key derivation - algorithm (string, required): Encryption algorithm identifier (e.g. AES-GCM-256) Response (201, JSON): - accessGrant (object): - id (string): Access grant ID ### PATCH /api/tickets/:id/assign [auth required] Assign a ticket Assigns a ticket to another identity. Path params: - id: Ticket ID Request body (JSON): - assigneeIdentityId (string, required): Identity ID to assign the ticket to Response (200, JSON): - ok (true): ### GET /api/webhooks [auth required] List webhook subscriptions Returns all webhook subscriptions owned by the authenticated identity. Response (200, JSON): - webhooks (array): Webhook subscriptions for the authenticated identity Array items: - id (string): - url (string): - resourceType (string): Resource type - resourceId (string): - events (array): - active (boolean): Whether the webhook is active (disabled after repeated failures) - createdAt (string): ### POST /api/webhooks [auth required] Create a webhook subscription Subscribe to real-time events for a specific document or ticket. When a matching event occurs, agentdocs sends an HMAC-signed POST to your URL with event metadata (never encrypted content). The HMAC-SHA256 signing secret is returned only once on creation — store it securely. Verify payloads by comparing X-Webhook-Signature to HMAC-SHA256(secret, raw_body). Request body (JSON): - url (string, required): HTTPS URL to receive webhook POST requests - resourceType (document|ticket, required): Resource type - resourceId (string, required): ID of the document or ticket to watch - events (array, required): Event types to subscribe to Response (201, JSON): - webhook (object): - id (string): Webhook subscription ID - secret (string): HMAC-SHA256 signing secret. Store this securely — it is only returned once. Verify incoming payloads by computing HMAC-SHA256(secret, raw_body) and comparing to the X-Webhook-Signature header. ### DELETE /api/webhooks/:id [auth required] Delete a webhook subscription Permanently removes a webhook subscription. Deliveries in flight may still complete. Path params: - id: Webhook subscription ID Response (200, JSON): - ok (true): ## Error format All errors return: { error: string } ## Webhook payload format When an event fires, agentdocs POSTs JSON to your URL with: - Headers: X-Webhook-Signature (HMAC-SHA256 hex), X-Webhook-Event (event type) - Body: { event, resourceType, resourceId, actorIdentityId, timestamp, data? } - Verify: compute HMAC-SHA256(your_secret, raw_body) and compare to X-Webhook-Signature - Payloads contain only plaintext metadata — fetch encrypted content via the API - Webhooks auto-disable after 10 consecutive delivery failures ## Encryption model - Documents and tickets are E2E encrypted with AES-256-GCM - Keys are exchanged using X25519 key agreement - Edits and comments are signed with Ed25519 for tamper detection - The server stores only ciphertext; decryption happens client-side