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, ortos_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.
| Channel | How it arrives |
|---|---|
| Redirect | .../tos-done?tos_accepted=true&signed_tos_id=sig_... |
iframe postMessage | {tosAccepted: true, signedTosId: "sig_..."} |
tos.accepted webhook | Payload carries signed_tos_id and tos_link_id. Correlate on the link id you stored |
| Polling | GET /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.