Core ConceptsTerms of Service

Terms of Service

Terms come before the customer: a hosted acceptance page, or a programmatic attestation from your own UI.

Terms first

A customer cannot be created without an accepted agreement. Creation requires exactly one of:

  • signed_tos_id, the receipt from a hosted acceptance, or
  • tos_acceptance: {"ip_address": "..."}, a programmatic attestation from your own UI.

Sending both, or neither, is a 422. Both paths record identical evidence: terms version, method, IP address, and a server-side timestamp.

Path A: hosted acceptance

Mint a link

curl -s $BASE/api/tos_links \
  -H "Api-Key: $KEY" -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"redirect_url": "https://yourapp.example/tos-done"}'
{
  "id": "tos_033y0AbCdEfGhIjKlMnOpQ",
  "object": "tos_link",
  "url": "https://sandbox.rasto.co/tos/accept?session_token=...",
  "tos_version": null,
  "status": "pending",
  "signed_tos_id": null,
  "customer_id": null,
  "created_at": "2026-08-26T09:14:02+00:00"
}

Store the link id against your signup session and send the user to url. It is a mobile-friendly page showing the Terms of Service and Privacy Policy with a single agree action, and it works full page or inside an iframe.

Receive the receipt

Acceptance mints a signed_tos_id (sig_...), delivered four ways. Use a fast one for the UX and the webhook as your source of truth.

ChannelHow it arrives
Redirect.../tos-done?tos_accepted=true&signed_tos_id=sig_...
iframe postMessage{tosAccepted: true, signedTosId: "sig_..."}
tos.accepted webhookPayload carries signed_tos_id and tos_link_id. Correlate on the link id you stored
PollingGET /api/tos_links/{id}, where signed_tos_id fills once accepted

Bind it at creation

Pass the receipt to POST /api/customers as signed_tos_id. Binding is strict: an unknown or unaccepted id is 400 invalid_signed_tos_id, and a receipt that already created a customer is 409 signed_tos_id_already_used. One acceptance, one customer.

Safety properties worth relying on: links never expire, double clicks and refreshes replay the same receipt idempotently, and the accept page records the end user's IP address and a server-side timestamp as evidence.

Path B: programmatic attestation

If you present our terms inside your own signup flow, attest inline at creation:

{
  "kind": "individual",
  "first_name": "Ana",
  "last_name": "Silva",
  "tos_acceptance": { "ip_address": "203.0.113.7" }
}

ip_address must be the end customer's real client IP, not your server's and not a proxy hop. It is the evidence that makes the attestation meaningful.

Re-acceptance and versioning

Every customer payload embeds a tos object:

"tos": {
  "status": "accepted",
  "tos_version": "2026-08-01",
  "accepted_at": "2026-08-26T09:14:40+00:00"
}

A terms-version bump never re-gates an existing customer. Continued use binds amendments, and the documents carry that clause. If you want an explicit fresh acceptance after a bump, send tos_acceptance (or a new signed_tos_id) on PATCH /api/customers/{id}. Sending one while the customer is already on the current version returns 409 tos_already_accepted, and that refusal rejects the whole patch: no other field lands either.

POST /api/tos_links optionally takes a customer_id for an existing customer. Acceptance then binds that customer directly, with no follow-up call. This is the hosted path for re-acceptance, and it is refused the same way (409) when the customer is already on the current version.