Skip to content

API Reference

The Covalent REST API is served under /api/v2 on your company's host. Every endpoint authenticates with an API key in the x-api-key header, and every answer, success or failure, comes in one JSON envelope.

Covalent serves each company from its own host, <company>.<app-domain>: the address your dashboard opens at. The host chooses the company. A request to a host that names no company is 404 NOT_FOUND.

The examples in these pages read the host and the key from two shell variables:

Terminal window
export COVALENT_URL="https://<company>.<app-domain>"
export COVALENT_API_KEY="covalence_pk_..."
curl "$COVALENT_URL/api/v2/customers" \
-H "x-api-key: $COVALENT_API_KEY"

The API is for server-to-server calls. It sends no CORS headers, so browsers cannot call it from another origin.

A success carries the resource in data, and meta:

{
"data": {
"id": "wallet_id",
"deleted": true
},
"meta": {
"apiVersion": 2,
"timestamp": "2026-10-09T12:00:00.000Z"
}
}

meta.apiVersion is always 2, and meta.timestamp is when the answer was made. An endpoint adds its own fields to meta after these two: a list adds its page, a transfer action the state it left, and an answer with masked personal data piiMasked: true.

data is the resource itself, not wrapped in its name: a wallet is data, not data.wallet. The one exception is creating or changing a webhook subscription, which answers data.webhook, beside data.secret when a new signing secret is issued.

A failure carries a message, a machine-readable code, details where there are any, and the same meta:

{
"error": "Transfer not found: transfer_id",
"code": "TRANSFER_NOT_FOUND",
"details": {
"resource": "transfer",
"id": "transfer_id"
},
"meta": {
"apiVersion": 2,
"timestamp": "2026-10-09T12:00:00.000Z"
}
}
  • error is a sentence for people. Match on code.
  • details is present only where it adds something: the issues found in a query or body, the transfers that block an erasure, the actions an endpoint takes.
  • The body has no retry flag. A failure that a later retry may fix (429, and some 503) carries a Retry-After header with the seconds to wait.

The only answer that is not JSON is a successful report download, which is the file itself. See Reports.

For every status and code, see Status Codes.

Header Sent with Meaning
api-version: 2 Every answer, file downloads included The API version.
cache-control: no-store Every answer Answers are never cached: some carry a signing secret.
Retry-After 429, and a 503 that a retry may fix Seconds to wait before retrying.
Allow 405, and OPTIONS The methods the path takes.
x-ratelimit-* A 429 from a rate limit See Rate Limits.
x-content-sha256 A report download The file's SHA-256, in hex.

Every answer also carries security headers: X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Strict-Transport-Security, and Content-Security-Policy: default-src 'none'; frame-ancestors 'none'.

  • Send JSON bodies with Content-Type: application/json. A body that is not JSON is 400 INVALID_JSON. An empty body reads as {}.
  • A body is at most 64 KiB (65,536 bytes). A larger one is 413 BODY_TOO_LARGE.
  • No text may contain the NUL character (U+0000), in the path, the query or the body: 400 VALIDATION_ERROR.
  • A query parameter sent twice is read by its first value.
  • A path the API does not have is 404 NOT_FOUND. A method a path does not take is 405 METHOD_NOT_ALLOWED, with Allow. OPTIONS answers 204 with Allow. HEAD is answered as GET without the body, except on a report download, where it is 405.
  • A POST, PUT, PATCH or DELETE that carries the dashboard's session cookie is refused with 403 CSRF_VALIDATION_FAILED, whatever key it carries.

Each API key belongs to one environment, test or live, chosen when the key is created. A request reads and writes that environment only: a record of the other environment is not found, and the answer does not say that it exists. No header or parameter chooses the environment.

Thresholds are the exception: one set applies to both environments. Changes that affect both environments (enabling a provider or an integration, changing a threshold) need a live key, else 403 LIVE_KEY_REQUIRED.

What a key sees follows its owner's role. A role without the pii:view permission (an analyst) gets personal data masked: customer names as initials, free text as ***, screening vendors' raw responses as null. Such an answer carries meta.piiMasked: true. Searches cannot reveal masked text: such a role searches customers by externalId and cases by case number only.

Lists answer one page at a time. Ask for a page size with limit, and for the next page with cursor:

Terminal window
curl "$COVALENT_URL/api/v2/transfers?limit=2" \
-H "x-api-key: $COVALENT_API_KEY"
{
"data": [
{ "id": "transfer_2", "state": "queued" },
{ "id": "transfer_1", "state": "completed" }
],
"meta": {
"apiVersion": 2,
"timestamp": "2026-10-09T12:00:00.000Z",
"cursor": "c1.1791540000000.transfer_1",
"hasMore": true,
"total": 7,
"limit": 2
}
}
Field Meaning
meta.cursor Send it as ?cursor= for the next page. null on the last page.
meta.hasMore Whether another page follows.
meta.total How many items match the filters, across all pages.
meta.limit The page size used.
  • A cursor is opaque. Send meta.cursor as you received it, URL-encoded. Most lists' cursors have the form c1.<milliseconds>.<id>: they name a position, so paging neither skips nor repeats an item, even while items are added.
  • Only a meta.cursor that the same list handed out is a cursor. On every list but /thresholds, whose cursor is a threshold's ID, an item's ID is not a cursor: it is refused with 400 INVALID_CURSOR. A value that is not shaped like a cursor at all may be refused with 400 INVALID_QUERY instead.
  • limit must be a whole number from 1 to the list's maximum. Anything else (abc, 0, -5, 7.9, a number over the maximum) is 400 INVALID_QUERY, and details.issues names each problem. The VASP search alone reads a limit over its maximum as the maximum.
  • A list refuses a parameter it does not take (?offset=10, a misspelt filter) and a filter value it does not know: 400 INVALID_QUERY. The rules, thresholds, VASP search and asset lists ignore parameters they do not take.
  • An empty value (?cursor=, ?limit=) is the same as none.
List Order Default limit Maximum Paging
/transfers, /customers, /wallets Newest first 50 100 cursor
/rules Newest first 50 100 cursor
/cases, /cases/:id/notes, /cases/:id/history Newest first 50 200 cursor
/reports Newest first 50 200 cursor
/webhooks, /webhooks/:id/deliveries Newest first 50 200 cursor
/activities, /audit-logs Newest first 50 200 cursor
/thresholds Jurisdiction, then type 50 100 cursor (the last threshold's ID)
/vasps/search Name 20 50 (a larger limit is read as 50) cursor
/assets Catalog order 50 200 offset

Each endpoint needs the scope shown on the API key, and the key's owner needs the matching role permission. See Authentication.

Method Endpoint Scope Description
GET /api/v2/customers customers:read List customers
POST /api/v2/customers customers:write Create a customer
POST /api/v2/customers/:id/deactivate customers:write Deactivate a customer
POST /api/v2/customers/:id/reactivate customers:write Reactivate a customer
POST /api/v2/customers/:id/erase customers:erase Irreversibly erase a customer's personal data
PUT /api/v2/customers/:id/travel-rule-identity customers:write Set the customer's attested Travel Rule identity
GET /api/v2/wallets wallets:read List wallets
POST /api/v2/wallets wallets:write Register a wallet
GET /api/v2/wallets/:id wallets:read Get a wallet
PATCH /api/v2/wallets/:id wallets:write Change a wallet's label
DELETE /api/v2/wallets/:id wallets:write Remove a wallet
POST /api/v2/wallets/verify wallets:verify Check whether an address is a registered wallet
Method Endpoint Scope Description
GET /api/v2/transfers transfers:read List transfers
POST /api/v2/transfers transfers:write Create an outgoing transfer
GET /api/v2/transfers/:id transfers:read Get a transfer
PATCH /api/v2/transfers/:id transfers:write Accept, reject or retry a transfer
GET /api/v2/transfers/:id/information-requests transfers:read Read a transfer's information requests
POST /api/v2/transfers/:id/information-requests transfers:write Act on a transfer's information requests
GET /api/v2/vasps/search transfers:read Search counterparty VASPs
GET /api/v2/assets transfers:read List the asset catalog
Method Endpoint Scope Description
GET /api/v2/cases cases:read List cases
POST /api/v2/cases cases:write Open a case
GET /api/v2/cases/:id cases:read Get a case
PATCH /api/v2/cases/:id cases:write Change a case
GET /api/v2/cases/:id/notes cases:read List a case's notes
POST /api/v2/cases/:id/notes cases:write Add a note
GET /api/v2/cases/:id/history cases:read List a case's history
POST /api/v2/cases/:id/resolve cases:write Resolve a case
GET /api/v2/report-types reports:read, strs:read Report types by kind and jurisdiction
GET /api/v2/reports reports:read, strs:read List reports
GET /api/v2/reports/:id reports:read, strs:read Get a report
GET /api/v2/reports/:id/download reports:read, strs:read, transfers:read Download a report as a PDF
GET /api/v2/rules rules:read List compliance rules
POST /api/v2/rules rules:write Create a rule
GET /api/v2/rules/:id rules:read Get a rule
PATCH /api/v2/rules/:id rules:write Change a rule
DELETE /api/v2/rules/:id rules:write Deactivate a rule
GET /api/v2/thresholds thresholds:read List regulatory thresholds
POST /api/v2/thresholds thresholds:write Create a threshold (live key)
GET /api/v2/thresholds/:id thresholds:read Get a threshold
PATCH /api/v2/thresholds/:id thresholds:write Change a threshold (live key)
Method Endpoint Scope Description
GET /api/v2/providers providers:read List Travel Rule providers and their settings
GET /api/v2/providers/:name providers:read Get a provider
PUT /api/v2/providers/:name providers:write Save a provider's credentials
PATCH /api/v2/providers/:name providers:write Enable or disable a provider (live key)
POST /api/v2/providers/:name/test providers:write Test a provider's connection
GET /api/v2/integrations integrations:read List screening integrations and their settings
GET /api/v2/integrations/:name integrations:read Get an integration
PUT /api/v2/integrations/:name integrations:write Save an integration's credentials
PATCH /api/v2/integrations/:name integrations:write Enable or disable an integration (live key)
POST /api/v2/integrations/:name/test integrations:write Test an integration's connection
GET /api/v2/webhooks webhooks:read List webhook subscriptions
POST /api/v2/webhooks webhooks:write Create a subscription
GET /api/v2/webhooks/:id webhooks:read Get a subscription, its stats and latest deliveries
PATCH /api/v2/webhooks/:id webhooks:write Change a subscription or rotate its secret
POST /api/v2/webhooks/:id/test webhooks:write Send a test event
GET /api/v2/webhooks/:id/deliveries webhooks:read List a subscription's deliveries
POST /api/v2/webhooks/:id/deliveries/:deliveryId/retry webhooks:write Send a failed delivery again
Method Endpoint Scope Description
GET /api/v2/activities activity:read Read the activity feed
GET /api/v2/audit-logs audit:read Read the audit log

These are done in the dashboard, not through the API: creating and revoking API keys, managing team members, promoting test rules to live, drafting, editing, submitting and filing reports, cancelling a queued transfer, and adding or revoking wallet ownership proofs.