Skip to content

Customers

Customers are your end users: the parent records of wallets, and the originators of outgoing transfers. Each customer belongs to the API key's environment, and gets a Veriscope keypair when it is created; only the public key is ever returned.

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 Erase a customer's personal data
PUT /api/v2/customers/:id/travel-rule-identity customers:write Set the customer's attested Travel Rule identity

A customer of the other environment is not found: 404 CUSTOMER_NOT_FOUND.

Terminal window
curl "$COVALENT_URL/api/v2/customers?limit=50" \
-H "x-api-key: $COVALENT_API_KEY"

Query parameters:

Parameter Description
includeDeactivated true adds deactivated and erased customers; false, the default, lists active customers only.
search Part of the externalId. A key whose owner's role has pii:view also searches names.
limit Items per page, 1 to 100, default 50.
cursor meta.cursor of the previous page.

Any other parameter or value is 400 INVALID_QUERY. status is not a parameter of this list.

{
"data": [
{
"id": "customer_id",
"externalId": "cust_123",
"name": "Jane Doe",
"publicKey": "02989c0b76cb563971fdc9bef31ec06c3560f3249d6ee9e5d83c57625596e05f6f",
"environment": "live",
"walletCount": 1,
"createdAt": "2026-10-08T16:00:00.000Z",
"updatedAt": "2026-10-08T16:00:00.000Z",
"deactivatedAt": null,
"erasedAt": null
}
],
"meta": {
"apiVersion": 2,
"timestamp": "2026-10-09T12:00:00.000Z",
"cursor": null,
"hasMore": false,
"total": 1,
"limit": 50
}
}
Field Description
id The customer's ID.
externalId Your reference for the customer, or null.
name The customer's name, or null. Masked to initials (J*** D***) for a role without pii:view, with meta.piiMasked: true.
publicKey The customer's Veriscope public key, or null.
environment test or live: the key's.
walletCount How many wallets are registered to the customer.
deactivatedAt When the customer was deactivated, or null while active.
erasedAt When the customer's personal data was erased, or null.
Terminal window
curl -X POST "$COVALENT_URL/api/v2/customers" \
-H "x-api-key: $COVALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalId": "cust_123",
"name": "Jane Doe",
"metadata": {
"segment": "retail"
}
}'
Field Required Description
externalId No Your reference: text of at most 255 characters, unique in the environment.
name No Text of at most 255 characters.
metadata No Any JSON.

The answer is 201 with the customer itself:

{
"data": {
"id": "customer_id",
"externalId": "cust_123",
"name": "Jane Doe",
"publicKey": "02989c0b76cb563971fdc9bef31ec06c3560f3249d6ee9e5d83c57625596e05f6f",
"environment": "live",
"createdAt": "2026-10-09T12:00:00.000Z"
},
"meta": {
"apiVersion": 2,
"timestamp": "2026-10-09T12:00:00.000Z"
}
}

The private key is encrypted and stored, and never returned.

An externalId already used in the environment is refused, whether its customer is active or deactivated:

{
"error": "Customer with externalId \"cust_123\" already exists in live environment",
"code": "EXTERNAL_ID_IN_USE",
"details": {
"customerId": "customer_id",
"deactivated": false
},
"meta": {
"apiVersion": 2,
"timestamp": "2026-10-09T12:00:00.000Z"
}
}

A body that is not a JSON object, or an externalId or name that is not text of at most 255 characters, is 400 VALIDATION_ERROR.

Terminal window
curl -X POST "$COVALENT_URL/api/v2/customers/customer_id/deactivate" \
-H "x-api-key: $COVALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "Customer account closed"
}'

The body may be empty. A reason is kept to its first 1,000 characters.

{
"data": {
"id": "customer_id",
"deactivatedAt": "2026-10-09T12:00:00.000Z",
"deactivatedBy": "user_id",
"deactivationReason": "Customer account closed"
},
"meta": {
"apiVersion": 2,
"timestamp": "2026-10-09T12:00:00.000Z"
}
}

Deactivation is reversible. The customer keeps its data and transfer history, but it leaves the default list, cannot originate a transfer and cannot take a new wallet. A customer that is already deactivated is answered as it is, with id and deactivatedAt only.

Terminal window
curl -X POST "$COVALENT_URL/api/v2/customers/customer_id/reactivate" \
-H "x-api-key: $COVALENT_API_KEY"

The answer is { "id": "customer_id", "deactivatedAt": null }, also for a customer that was already active. An erased customer cannot be reactivated: 409 CUSTOMER_ERASED.

Terminal window
curl -X POST "$COVALENT_URL/api/v2/customers/customer_id/erase" \
-H "x-api-key: $COVALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "Verified erasure request"
}'

Erasure answers a data subject's request (GDPR Article 17). It needs the customers:erase scope and is irreversible:

  • reason is required (400 REASON_REQUIRED without it), and kept to 1,000 characters.
  • The customer's personal data (name, reference, keys, metadata) is nulled in place.
  • The customer is deactivated, and its transfers stay linked to it.
{
"data": {
"id": "customer_id",
"erasedAt": "2026-10-09T12:00:00.000Z",
"erasedBy": "user_id",
"deactivatedAt": "2026-10-09T12:00:00.000Z"
},
"meta": {
"apiVersion": 2,
"timestamp": "2026-10-09T12:00:00.000Z"
}
}

A customer that is already erased answers 200 with its id, its erasedAt and alreadyErased: true.

Erasure is refused with 409 while an obligation holds the records:

Code When Details
LEGAL_HOLD A legal hold or a statutory retention obligation covers the customer's records. The answer does not say which. None
IN_FLIGHT The customer has transfers that have not reached a final state. Wait for them to settle. activeTransferIds
{
"error": "Cannot erase customer with 2 in-flight transfer(s). Wait for them to settle or cancel them first.",
"code": "IN_FLIGHT",
"details": {
"activeTransferIds": ["transfer_1", "transfer_2"]
},
"meta": {
"apiVersion": 2,
"timestamp": "2026-10-09T12:00:00.000Z"
}
}

A customer's Travel Rule identity is the IVMS101 data your KYC or KYB process has attested. Covalent sends it to counterparties on the customer's behalf:

  • as the beneficiary of an incoming transfer, through Notabene and Global Travel Rule; CodeVASP checks the beneficiary names the originator sent against it;
  • as the originator of an outgoing transfer sent through Notabene, Global Travel Rule or Sygna Bridge.

Without a verified identity, that data is not sent, and the transfer cannot complete its exchange through that provider. A customer's name is not evidence of identity, and is never sent in its place.

Terminal window
curl -X PUT "$COVALENT_URL/api/v2/customers/customer_id/travel-rule-identity" \
-H "x-api-key: $COVALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "verified",
"persons": [
{
"naturalPerson": {
"name": {
"nameIdentifier": [
{
"primaryIdentifier": "Doe",
"secondaryIdentifier": "Jane",
"nameIdentifierType": "LEGL"
}
]
},
"countryOfResidence": "US"
}
}
]
}'
Field Required Description
status Yes verified, unverified or blocked. Only a verified identity is sent to counterparties.
persons Yes IVMS101 persons, 1 to 20: one natural person, or a legal person followed by its natural-person representatives.

No other field is accepted. The answer is { "id": "customer_id", "status": "verified" }. The identity is encrypted before it is stored and is never returned; the audit log records its status only.

  • The customer must be active: a deactivated or erased customer is 404 CUSTOMER_NOT_FOUND.
  • An invalid identity is 400 VALIDATION_ERROR, with details.issues naming each problem.
  • A customer whose metadata is not a JSON object cannot hold an identity: 409 METADATA_NOT_OBJECT.
  • A concurrent change to the customer's metadata is 409 CONFLICT: retry.
  • Only this endpoint sets an identity. A travelRuleIdentity key sent in a customer's metadata is not an attestation: the customer has no usable identity until this endpoint sets one.

For every customer error code, see Status Codes.