Skip to content
API reference

Build on the Coveton API.

A REST API for identity, organizations, devices, encrypted transport, files and billing. JSON over HTTPS, with the zero-knowledge guarantee preserved end to end.

https://api.coveton.com/v1Zero-knowledge transport

Getting started

Introduction

The Coveton API is a REST interface for everything around your encrypted communication: user identity, organizations, teams and roles, device registration, session management, encrypted message transport, file storage and billing. It is the same API that powers the Coveton mobile app and admin dashboard.

Every request and response is JSON over HTTPS. Resources are organized around predictable, plural nouns, standard HTTP verbs describe intent, and HTTP status codes describe outcome. There are no SOAP envelopes, no GraphQL schema to learn, and no client library required to get started: any HTTP client works today.

A note on encryption. The API moves ciphertext, never plaintext. End-to-end encryption happens on the client. The REST surface handles transport, identity and orchestration; the cryptography belongs to the Coveton client SDK. See the zero-knowledge model.

Official and community SDKs are planned for cURL workflows plus JavaScript, TypeScript, Python, PHP, Ruby, Go, Java, C#, Rust, Swift and Kotlin. Until they ship, the examples throughout this reference show idiomatic, copy-pasteable requests in all twelve. Use the language switcher on any code block; your choice applies to every sample on the page.

Quickstart

Create an account, exchange your credentials for tokens, then call an authenticated endpoint. The login response returns a short-lived accessToken and a rotating refreshToken.

register
curl -X POST https://api.coveton.com/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "ada@example.com",
    "password": "a-strong-passphrase",
    "displayName": "Ada Lovelace"
  }'

With an accessToken in hand, every other call carries it in the Authorization header. Fetch the current user to confirm you are authenticated:

GET /v1/auth/me
curl https://api.coveton.com/v1/auth/me \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Authentication

Coveton uses bearer tokens. Send your access token on every authenticated request:

Authorization: Bearer <accessToken>

Access tokens are short-lived JWTs that expire after 15 minutes. Refresh tokens are long-lived but rotate on every use: each call to POST /v1/auth/refresh returns a fresh pair and invalidates the refresh token you just presented. Store the new pair and discard the old. If a refresh token is ever replayed, the whole token family is revoked.

POST /v1/auth/refresh
curl -X POST https://api.coveton.com/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{ "refreshToken": "'"$REFRESH_TOKEN"'" }'

Calling POST /v1/auth/logout revokes the presented refresh token. To sign up or sign in with an identity provider, redirect the user to GET /v1/auth/oauth/google or GET /v1/auth/oauth/github; the callback completes the same token exchange.

Zero-knowledge model

Coveton is end-to-end encrypted, and that property is enforced at the protocol level, not by policy. Encryption and decryption happen on the client. The server stores and serves opaque ciphertext together with per-device key envelopes, and never holds the keys to read either. There is no server-side capability, technical or legal, to produce your plaintext.

  • Keys stay on devices. Identity and message keys are generated and held by clients. Private keys are never transmitted to or stored by Coveton.
  • The server sees envelopes. A message carries one envelope per recipient device. Each envelope is the message key sealed to that device's public key. The server routes them; it cannot open them.
  • Ciphertext in, ciphertext out. What you POST is what other devices GET. The API never transforms message bodies.

The cryptographic key management, the Signal-style ratchet, and envelope sealing are implemented by the Coveton client SDK. Integrators building E2E flows should use it to produce ciphertext and envelopes rather than rolling their own crypto. The REST API documented here handles everything around that: transport, identity, organizations, devices, presence, files and billing.

Core concepts

Base URL & versioning

All requests go to a single host, and every endpoint lives under the /v1 prefix. Requests and responses are JSON, sent and received over HTTPS only. Plain HTTP is rejected.

https://api.coveton.com/v1

The major version is pinned in the path. Backwards-incompatible changes ship under a new prefix (for example a future /v2); additive changes such as new fields or new endpoints are made in place. Treat unknown JSON fields as forwards compatible and ignore the ones you do not use.

Errors

Coveton uses conventional HTTP status codes and returns a consistent error envelope on failure. The code is a stable machine-readable string, message is human-readable, and details is present on validation failures.

error envelope
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
  "error": {
    "code": "VALIDATION",
    "message": "Request validation failed.",
    "details": [
      { "path": "email", "message": "must be a valid email" }
    ]
  }
}

Error codes

POSTBAD_REQUEST400. Malformed request or unsupported parameters.
GETUNAUTHORIZED401. Missing, expired or invalid access token.
GETFORBIDDEN403. Authenticated, but lacking permission for this resource.
GETNOT_FOUND404. The resource does not exist or is not visible to you.
POSTCONFLICT409. A state conflict, such as a duplicate or stale write.
POSTVALIDATION422. The body failed validation; see details[].
GETRATE_LIMITED429. Too many requests; back off and retry.
GETINTERNAL500. An unexpected server error; safe to retry idempotent calls.

The first column reuses the method chip purely as a colored marker; these rows describe codes, not endpoints.

Rate limits

The API allows 120 requests per minute, enforced both per IP address and per access token. Exceeding the limit returns 429 with the RATE_LIMITED code. Every response carries the current budget in its headers so you can throttle proactively rather than reactively.

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 1718900000
Retry-After: 12

On a 429, honor Retry-After (seconds) before retrying. For bursty workloads, prefer exponential backoff with jitter and keep a single token per process to stay within the per-token budget.

Pagination

List endpoints return JSON arrays. High-volume, time-ordered collections such as messages use cursor-based pagination with limit and a before cursor. Pass the id of the oldest item you have seen as before to walk backwards through history; stop when fewer than limit items come back.

GET /v1/chats/:id/messages
curl "https://api.coveton.com/v1/chats/$CHAT_ID/messages?limit=50&before=$OLDEST_MESSAGE_ID" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Webhooks

Coveton both sends and receives signed webhooks. Outbound webhooks notify your systems of platform events; inbound payment webhooks from your processor are delivered to /v1/webhooks/stripe, /v1/webhooks/paystack and /v1/webhooks/flutterwave, where Coveton verifies the provider signature before acting.

Every outbound delivery is signed. Verify it before trusting the body: recompute an HMAC over the raw request bytes using your endpoint signing secret, then compare against the signature header in constant time. Reject anything that does not match, and reject deliveries whose timestamp is outside a few minutes of now to blunt replays.

verify signature
# Coveton signs each delivery; verify in your handler, not via curl.
# Header: X-Coveton-Signature: t=1718900000,v1=hexhmac
# Signed payload string: "{t}.{rawBody}"  (HMAC-SHA256, hex)

Idempotency

Network calls fail in ambiguous ways: a request can succeed on the server while the response is lost in transit. To make writes safe to retry, send a unique Idempotency-Key header on POST requests that create resources (sending a message, creating an organization, starting a checkout). Coveton records the first result for a key and replays it on any retry that carries the same key, so a resource is never created twice.

Idempotency-Key
curl -X POST https://api.coveton.com/v1/messages \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 9f1c2b7e-3a4d-4f8a-9c2e-1b0d5e6f7a8b" \
  -d @message.json

API reference

Auth

Account creation, sessions and OAuth. These endpoints are public except for those that read the current identity.

POST/v1/auth/registerCreate an account; returns accessToken and refreshToken.
POST/v1/auth/loginExchange email and password for a token pair.
POST/v1/auth/refreshRotate the refresh token and mint a new access token.
POST/v1/auth/logoutRevoke the presented refresh token.
GET/v1/auth/meReturn the authenticated account.
POST/v1/auth/forgot-passwordSend a password reset email.
POST/v1/auth/reset-passwordSet a new password using a reset token.
GET/v1/auth/oauth/googleBegin the Google OAuth sign-in flow.
GET/v1/auth/oauth/githubBegin the GitHub OAuth sign-in flow.

Log in

POST /v1/auth/login
curl -X POST https://api.coveton.com/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{ "email": "ada@example.com", "password": "a-strong-passphrase" }'

Users

Manage your own profile and look up other users to start conversations.

GET/v1/users/meFetch your profile and preferences.
PATCH/v1/users/meUpdate profile fields such as displayName or avatar.
GET/v1/users/searchSearch users by handle or email (e.g. ?q=ada).

Organizations

Organizations are the top-level tenant. They own members, teams, billing and audit logs. Creating one makes you its owner.

POST/v1/organizationsCreate an organization; the caller becomes owner.
GET/v1/organizationsList organizations you belong to.
GET/v1/organizations/:idFetch a single organization.
PATCH/v1/organizations/:idUpdate name, settings or branding.
GET/v1/organizations/:id/membersList members and their roles.
PATCH/v1/organizations/:id/members/:userIdChange a member's role or status.
POST/v1/organizations/:id/suspendSuspend the organization (owner only).

Create an organization

POST /v1/organizations
curl -X POST https://api.coveton.com/v1/organizations \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Analytical Engine Co", "slug": "analytical-engine" }'

Teams & roles

Within an organization, structure is expressed through teams, departments, roles, granular permissions and invites. Each resource supports standard CRUD. Roles bind a set of permissions; assigning a role to a member grants those permissions.

POST/v1/teamsCreate a team. GET/PATCH/DELETE /v1/teams/:id for the rest.
POST/v1/departmentsCreate a department. Full CRUD under /v1/departments/:id.
POST/v1/rolesDefine a role. Full CRUD under /v1/roles/:id.
GET/v1/permissionsList assignable permission keys.
POST/v1/invites/:organizationIdCreate an invite for an organization.
POST/v1/invites/acceptAccept an invite by token and join the org.

Devices

Every device that participates in encrypted messaging registers its public key so other members can seal envelopes to it. Removing a device revokes its ability to receive new messages.

GET/v1/devicesList your registered devices and their public keys.
POST/v1/devicesRegister a device with its public key bundle.
DELETE/v1/devices/:idRemove a device and revoke its keys.

Sessions

Review active sessions across devices and revoke any of them remotely.

GET/v1/sessionsList active sessions with location and last-seen data.
DELETE/v1/sessions/:idRevoke a session and sign out that device.

Chats

A chat is a conversation between devices, either one-to-one or a group. Messages belong to a chat and are listed newest-first with cursor pagination.

POST/v1/chatsCreate a one-to-one or group chat.
GET/v1/chatsList chats you participate in.
GET/v1/chats/:idFetch a single chat and its participants.
GET/v1/chats/:id/messagesList messages (?limit=&before= cursor).

Messages

Messages carry ciphertext plus one envelope per recipient device. The client SDK produces both; the API only transports them. Each envelope holds a deviceId and an opaque header (the sealed message key for that device). An optional expiresAt turns the message into a self-destructing one.

POST/v1/messagesSend a message: { chatId, contentType, ciphertext, envelopes[], expiresAt? }.
PATCH/v1/messages/:idEdit a message (replaces ciphertext and envelopes).
DELETE/v1/messages/:idDelete a message for all participants.
POST/v1/messages/:id/reactionsAdd or toggle a reaction on a message.

Send an encrypted message

The body below is what the SDK hands you after encryption: opaque ciphertext and a sealed envelope per device. Coveton stores and routes it verbatim.

POST /v1/messages
curl -X POST https://api.coveton.com/v1/messages \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "chatId": "chat_8f2a",
    "contentType": "text/plain",
    "ciphertext": "BASE64_OPAQUE_CIPHERTEXT",
    "envelopes": [
      { "deviceId": "dev_a1", "header": "SEALED_KEY_FOR_DEVICE_A1" },
      { "deviceId": "dev_b2", "header": "SEALED_KEY_FOR_DEVICE_B2" }
    ],
    "expiresAt": "2026-06-21T12:00:00Z"
  }'

Files

Files are uploaded directly to storage, not through the API. Request a presigned URL, PUT the encrypted bytes to it, then reference the returned file id from a message. Downloads work the same way in reverse: ask for a short-lived download URL. Because the bytes you upload are already ciphertext, storage never sees plaintext.

POST/v1/files/presignGet a presigned upload URL and a file id.
GET/v1/files/:id/downloadGet a short-lived download URL for a file.

Presign an upload

POST /v1/files/presign
curl -X POST https://api.coveton.com/v1/files/presign \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "filename": "report.enc", "contentType": "application/octet-stream", "size": 482711 }'

# Response: { "fileId": "file_92ab", "uploadUrl": "https://...", "expiresIn": 900 }
# Then PUT the encrypted bytes straight to uploadUrl.

Subscriptions & billing

Each organization carries a subscription. Start a hosted checkout to begin or change a plan; the processor confirms the result through the payment webhooks described above. Paid invoices are available for reconciliation.

GET/v1/subscriptions/:organizationIdFetch the organization's current subscription.
POST/v1/subscriptions/:organizationId/checkoutCreate a hosted checkout session for a plan.
GET/v1/billing/:organizationId/invoicesList invoices for the organization.

Create a checkout

POST /v1/subscriptions/:organizationId/checkout
curl -X POST https://api.coveton.com/v1/subscriptions/$ORG_ID/checkout \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "plan": "team_annual", "provider": "stripe", "successUrl": "https://app.example.com/billing" }'

# Response: { "checkoutUrl": "https://checkout.stripe.com/..." }

Notifications

List in-app notifications and mark them as read.

GET/v1/notificationsList notifications, newest first.
POST/v1/notifications/readMark notifications as read by id, or all.

Webhooks

Payment processors deliver events to these endpoints. Coveton verifies the provider signature before processing; unsigned or stale deliveries are rejected. See Webhooks for verification details.

POST/v1/webhooks/stripeReceive and verify Stripe events.
POST/v1/webhooks/paystackReceive and verify Paystack events.
POST/v1/webhooks/flutterwaveReceive and verify Flutterwave events.

Audit logs

Audit logs are append-only records of security-relevant actions across an organization: membership changes, role grants, device removals, session revocations and billing events. Filter by organization.

GET/v1/audit-logs?organizationIdList audit events for an organization.

The API is REST and JSON, so any HTTP client works today. SDKs for the twelve languages above are planned; until then, the samples here are production-shaped starting points. Talk to us about early access