Terms and Conditions Acceptance
Each end user, sub-accounts included, has to accept Avenia's own Terms of Use and Privacy Policy. Avenia publishes these documents as a versioned set (a bundle). When a new version is published, every user is asked to accept it again.
You can get a user's acceptance in two ways:
- Through the API (this page): read the current terms, show the documents to the user in your own interface, and send the acceptance.
- Through the hosted page: send the user to the Avenia acceptance page returned in
url. The page shows the documents and records the acceptance itself. See Using the hosted page instead.
{user-id} in the paths below is the Avenia account ID of the user who accepts:
- for a sub-account, its
subAccountId(theidreturned when you created the sub-account); - for your main account, the top-level
idofGET /v2/account/account-infosent withoutsubAccountId.
These two endpoints take the account ID in the path. They don't use the subAccountId query parameter.
Accepting through the API
The flow has three steps:
- Read the terms with
GET /v2/terms/{user-id}. Keepbundle.versionandacceptanceToken. - Show the documents to the user and let them accept in your interface.
- Send the acceptance with
POST /v2/terms/{user-id}/accept, using thebundle.versionandacceptanceTokenfrom step 1.
Both endpoints are public: they don't take an API key or a bearer token. The account ID in the path identifies the user, so treat it as you treat any other account identifier.
Step 1: Read the terms
Returns the current version of the terms, its documents, the user's standing against it, and the token the acceptance requires.
Endpoint
Path Parameters
| Parameter | Type | Description |
|---|---|---|
user-id | string | The account ID of the user: a subAccountId, or your main account ID. |
Request example
curl -X GET "https://api.sandbox.avenia.io:10952/v2/terms/2eea0102-dabe-4ab6-8707-0817aa52dfe4"
JSON Response
A user who has never accepted:
{
"userId": "2eea0102-dabe-4ab6-8707-0817aa52dfe4",
"bundle": {
"key": "DEFAULT",
"version": 2
},
"documents": [
{
"order": 1,
"documentKey": "TERMS_OF_USE",
"documentId": "8a1f4f0e-3c2b-4d8e-9f61-2b7c5e0d9a13",
"displayName": "Terms of Use",
"contentUrl": "https://<documents-host>/<path>/terms-of-use.pdf",
"contentType": "application/pdf"
},
{
"order": 2,
"documentKey": "PRIVACY_POLICY",
"documentId": "c4d9e2a7-6b15-4f3a-8e0c-71a2d5f8b6e4",
"displayName": "Privacy Policy",
"contentUrl": "https://<documents-host>/<path>/privacy-policy.pdf",
"contentType": "application/pdf"
}
],
"currentVersion": 2,
"acceptedVersion": null,
"acceptedAt": null,
"url": "https://pay.sandbox.avenia.io/terms-conditions/2eea0102-dabe-4ab6-8707-0817aa52dfe4",
"required": false,
"acceptanceToken": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
Response Fields Explained
| Field | Type | Description |
|---|---|---|
userId | string | The account ID from the path. |
bundle.key | string | The identifier of the terms set. |
bundle.version | integer | The current version. Send it as bundleVersion when accepting. |
documents | array | The documents the user must be shown, in display order. |
documents[].order | integer | Display position, starting at 1. |
documents[].documentKey | string | The kind of document, for example TERMS_OF_USE or PRIVACY_POLICY. |
documents[].documentId | string | The ID of this document file. |
documents[].displayName | string | The title to show the user. |
documents[].contentUrl | string | Public link to the document. |
documents[].contentType | string | The file type: application/pdf. |
currentVersion | integer | The version in force, the same as bundle.version. |
acceptedVersion | integer or null | The latest version this user accepted. null when they never accepted. |
acceptedAt | string or null | When the user accepted acceptedVersion (ISO 8601, UTC). null when they never accepted. |
url | string | The Avenia-hosted acceptance page for this user. |
required | boolean | true when the user's KYC/KYB submissions are refused until they accept the current version. See When KYC waits for the acceptance. |
acceptanceToken | string | The token to send back when accepting. See The acceptance token. |
Reading the user's standing
acceptedVersion | Meaning | What to do |
|---|---|---|
null | The user never accepted. | Ask them to accept. |
Lower than currentVersion | A newer version was published since the user accepted. | Ask them to accept the new version. |
Equal to currentVersion | The user is up to date. | Nothing. |
The acceptance token
The acceptance requires the token from a recent read, so that an acceptance is only recorded for terms that were read first.
Step 2: Show the documents
Before accepting through the API, show the user every document in documents, in order, with its displayName and a link to (or an embedded view of) its contentUrl. The documents are PDFs. Then let the user agree explicitly, for example with a checkbox and a confirm button.
If you can't present the documents yourself, use the hosted page.
Step 3: Send the acceptance
Records the user's acceptance of the version they were shown.
Endpoint
Path Parameters
| Parameter | Type | Description |
|---|---|---|
user-id | string | The same account ID as in Step 1. |
Parameters
Required
| Field | Type | Description |
|---|---|---|
bundleVersion | integer | The version the user was shown: bundle.version from Step 1. |
acceptanceToken | string | The acceptanceToken from Step 1. |
Optional
| Field | Type | Description |
|---|---|---|
channel | string | How the acceptance was collected: API or EMBED_LINK. Defaults to API. When you accept through the API, send API or leave it out. |
clientMetadata | object | Your own data about this acceptance, for example your consent record ID. Must be a JSON object, up to 64 KiB. Avenia stores it as sent. |
Request body
{
"bundleVersion": 2,
"acceptanceToken": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"channel": "API",
"clientMetadata": {
"consentId": "your-consent-reference-123"
}
}
Request example
curl -X POST "https://api.sandbox.avenia.io:10952/v2/terms/2eea0102-dabe-4ab6-8707-0817aa52dfe4/accept" \
-H "Content-Type: application/json" \
-d '{
"bundleVersion": 2,
"acceptanceToken": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"channel": "API",
"clientMetadata": { "consentId": "your-consent-reference-123" }
}'
JSON Response
{
"bundleVersion": 2,
"acceptedAt": "2026-11-03T14:21:07.512Z"
}
| Field | Type | Description |
|---|---|---|
bundleVersion | integer | The version accepted. |
acceptedAt | string | When Avenia recorded the acceptance (ISO 8601, UTC, Avenia's server time). |
Accepting a version the user already accepted is safe: it returns the original bundleVersion and acceptedAt and records nothing new. It still needs a valid acceptanceToken.
After a successful acceptance, GET /v2/terms/{user-id} returns acceptedVersion equal to currentVersion.
Error handling
Errors use the standard error response: an HTTP status and a JSON body with error, and sometimes extraInfo.
Accept errors
| Status | error | Cause | What to do |
|---|---|---|---|
| 400 | InvalidFieldError: acceptanceToken is invalid | acceptanceToken missing or empty. | Send the token from the latest GET. |
| 400 | InvalidFieldError: bundleVersion is invalid | bundleVersion missing, zero or negative. | Send bundle.version from the GET. |
| 400 | InvalidFieldError: channel is invalid | channel isn't API or EMBED_LINK. | Send API or leave it out. |
| 400 | InvalidFieldError: clientMetadata is invalid | clientMetadata isn't a JSON object, or is larger than 64 KiB (extraInfo: too large). | Send a JSON object within the limit. |
| 400 | InvalidBodyError: could not parse body data | The body isn't valid JSON, or a field has the wrong type (for example bundleVersion as a string). | Fix the body. |
| 413 | request body too large | The whole body is larger than 128 KiB. | Shrink clientMetadata. |
| 403 | terms acceptanceToken is invalid or expired; read the terms again | The token isn't the user's live one: it expired (2 hours after the last GET), was never issued for this user, or was mistyped. | Call GET /v2/terms/{user-id} again and retry with the new acceptanceToken. |
| 409 | terms bundle version is not current | A newer version was published after the user read the terms. extraInfo holds the current version number, for example "3". | Call GET again, shoßw the user the new documents, and accept the new version. Don't resend the new version number without showing the new documents. |
| 404 | not found | No account with this ID. | Check the ID in the path. |
| 404 | no terms bundle published | Avenia has no terms version published yet. | Nothing to accept; KYC isn't blocked meanwhile. |
Example of a 409:
{
"error": "terms bundle version is not current",
"extraInfo": "3"
}
Read errors
GET /v2/terms/{user-id} returns 404 with not found for an unknown account ID, and 404 with no terms bundle published when there is nothing to accept yet.
KYC/KYB submission errors
A submission refused for a missing acceptance returns 403 with user has not accepted the current terms and conditions and the acceptance page URL in extraInfo. See Which submissions are checked.
Using the hosted page instead
The url field from GET /v2/terms/{user-id} (and extraInfo of the KYC 403) is an Avenia-hosted acceptance page for that user:
https://pay.sandbox.avenia.io/terms-conditions/{user-id}
Send the user to it, for example by opening it in a browser or a web view. The page shows the current documents and records the acceptance itself, from the user's own browser, with channel EMBED_LINK. You don't call the accept endpoint in this case. To confirm the acceptance, call GET /v2/terms/{user-id} and check that acceptedVersion equals currentVersion.
In production the page is on https://pay.avenia.io.