TOS Acceptance

Collect Terms of Service and Privacy Policy acceptance from your users.

Overview

Before an account can transact, the user must accept Due's Terms of Service (TOS) and Privacy Policy. There are two ways to handle this:

  • Due-hosted - Redirect the user to a Due-hosted acceptance page using the link field. Due handles presenting the documents and collecting acceptance. This is the simplest integration.
  • Manual - Present the documents yourself using the documentLinks, collect the user's real IP address, and submit their acceptance via the API.

Both flows start the same way: create an account and extract the tos object from the response.


Step 1 - Create an Account

Create an account via Create account. The response includes a tos object:

{
  "id": "acct_...",
  // ...
  "tos": {
    "id": "ta_...",
    "entityName": "DUE LTD",
    "status": "pending",
    "link": "/tos/token_1234567890",
    "documentLinks": {
      "tos": "https://link.to/terms_of_service",
      "privacyPolicy": "https://link.to/privacy_policy"
    },
    "acceptedAt": null,
    "token": "token_1234567890"
  }
}

TOS Object Fields:

FieldTypeDescription
statusstringCurrent TOS status. Either pending or accepted.
linkstringRelative URL to the Due-hosted TOS acceptance page. Use this for the hosted flow.
documentLinks.tosstringURL to the Terms of Service PDF.
documentLinks.privacyPolicystringURL to the Privacy Policy PDF.
tokenstringOne-time token used as the path parameter in the manual acceptance request.
entityNamestringName of the legal entity issuing the terms.
acceptedAtstring | nullISO 8601 timestamp of acceptance, or null if pending.

Option A - Due-Hosted Acceptance

The simplest integration. Redirect the user to the Due-hosted acceptance page using the link field from the tos object:

https://app.due.network.network/tos/token_1234567890

Due handles everything: presenting the TOS and Privacy Policy documents, collecting the user's IP address, and recording the acceptance.

Once the user accepts, the tos.status on the account transitions to accepted.

Option B - Manual Acceptance

If you need full control over the user experience, you can handle TOS acceptance in your own UI.

Step 2 - Present the Documents

Display the TOS and Privacy Policy to the user using the URLs from documentLinks. These are publicly accessible S3 links to PDF documents.

Both documents must be presented before collecting acceptance:

  • Terms of Service - documentLinks.tos
  • Privacy Policy - documentLinks.privacyPolicy

The specific legal entity and document versions are determined by the user's country at account creation time.

Step 3 - Collect the Client IP Address

Important: Real Client IP Required

The ipAddress parameter must contain the actual client's IP address, not the IP of any intermediate proxy, load balancer, or server.

If your application sits behind a proxy or load balancer, you need to extract the real client IP from headers such as X-Forwarded-For, X-Real-IP, or similar, depending on your infrastructure configuration.

Step 4 - Submit TOS Acceptance

Request

POST /v1/tos/{token}

Path Parameters:

ParameterTypeRequiredDescription
tokenstringYesThe TOS token from Step 1.

Headers:

HeaderTypeRequiredDescription
AuthorizationstringYesBearer token.

Body:

{
  "ipAddress": "203.0.113.42"
}
FieldTypeRequiredDescription
ipAddressstringYesThe real IP address of the user accepting the terms.

Response

{
  "id": "ta_...",
  "entityName": "DUE LTD",
  "status": "accepted",
  "link": "/tos/token_1234567890",
  "documentLinks": {
    "tos": "https://link.to/terms_of_service",
    "privacyPolicy": "https://link.to/privacy_policy"
  },
  "acceptedAt": "2026-02-04T22:26:24.30097021Z",
  "token": "token_1234567890"
}

Retrieving TOS Data

You can retrieve the current TOS state at any time:

GET /v1/tos/{token}

Returns the same TOS acceptance object shown above.

Webhook Events

When TOS is accepted, a tos_accepted webhook event is dispatched containing the full account object:

{
  "id": "evt_...",
  "type": "bp.tos_accepted",
  "data": {
    "id": "acct_...",
    "type": "individual",
    "name": "John Snow",
    "email": "[email protected]",
    "country": "US",
    "category": "self-employed",
    "status": "passed",
    "kyc": {
      "status": "passed",
      "link": "http://link.to/kyc",
    },
    "tos": {
      "id": "ta_...",
      "entityName": "DUE LTD",
      "status": "accepted",
      "link": "/tos/token_1234567890",
      "documentLinks": {
        "tos": "https://link.to/terms_of_service",
        "privacyPolicy": "https://link.to/privacy_policy"
      },
      "acceptedAt": "2026-02-04T22:26:24.30097021Z",
      "token": "token_1234567890"
    }
  },
  "occurredAt": "2026-02-04T22:28:35.398380671Z",
  "attemptedAt": "2026-02-04T22:28:35.40067069Z"
}

Example integration

# Step 1 - Fetch account to get TOS data
ACCOUNT=$(curl -s -X GET "https://api.due.network/v1/accounts/ACCOUNT_ID" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json")

TOKEN=$(echo "$ACCOUNT" | jq -r ".tos.token")

echo "TOS:            $(echo "$ACCOUNT" | jq -r ".tos.documentLinks.tos")"
echo "Privacy Policy: $(echo "$ACCOUNT" | jq -r ".tos.documentLinks.privacyPolicy")"

# Step 2 - Present the document links to the user

# Step 3 - Submit TOS acceptance with the user's real IP
curl -X POST "https://api.due.network/v1/tos/$TOKEN" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ipAddress": "203.0.113.42"}'
import requests

API_BASE = "https://api.due.network"
API_KEY = "YOUR_API_KEY"

def accept_tos(account_id: str, client_ip: str) -> dict:
    # Step 1 - Fetch account to get TOS data
    account = requests.get(
        f"{API_BASE}/v1/accounts/{account_id}",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json"
        }
    )
    account.raise_for_status()
    account = account.json()

    tos = account["tos"]
    token = tos["token"]

    # Step 2 - Present documents to user
    tos_url = tos["documentLinks"]["tos"]
    privacy_url = tos["documentLinks"]["privacyPolicy"]
    # Display these PDFs to the user in your UI

    # Step 3 - Submit TOS acceptance with the user's real IP
    response = requests.post(
        f"{API_BASE}/v1/tos/{token}",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json"
        },
        json={
            "ipAddress": client_ip
        }
    )
    response.raise_for_status()
    result = response.json()

    if result["status"] == "accepted":
        return {
            "status": "accepted",
            "acceptedAt": result["acceptedAt"]
        }

    return {"status": "pending"}