Authentication
API keys, environments, permissions, IP allowlists, expiry and rate limits.
Send the key on every request
Put your consumer API key in the X-TuCOPRamp-Key header, or send it as a Bearer token. Both
work the same way.
curl https://api.ramp.tucop.xyz/consumer \
--header "X-TuCOPRamp-Key: $TUCOPRAMP_API_KEY"
curl https://api.ramp.tucop.xyz/consumer \
--header "Authorization: Bearer $TUCOPRAMP_API_KEY"
- Only a Bearer token that starts with
tcr_is read as an API key. Other Bearer tokens (the session tokens some endpoints use) are left alone. - If you send both headers, they must carry the same key. Two different keys get
400 api_key_conflict. - On an endpoint that also needs a session token in
Authorization, put the key inX-TuCOPRamp-Key.
The health probes (/health/live, /health/ready) do not need a key. Every other endpoint in
these docs does. Platform endpoints that act for a person (/parties,
/destinations, /quotes, /payments, /limits, /withdrawals,
/deposits) also need a party session in X-TuCOPRamp-Party-Session, next to the key; see
Parties and sessions. When TuCOP linked your integrator's own payments provider account, all of
them except POST /parties also accept the key alone; see
Pay from your own payments provider account.
Key format
tcr_live_sk_AbCdEfGhIjKlMnOp.<secret><checksum>
| | | | | |
| | | | | 6 characters (base62): CRC-32 of everything before it
| | | | secret, 43 characters (base64url)
| | | public id, 16 characters (base64url)
| | key type: sk (secret key)
| environment: live or test
prefix
- The public id is not secret. TuCOP uses it to find your key, and
GET /consumerreturns it asapi_key.public_id. - The full key is shown once, when TuCOP issues it. TuCOP stores only a hash of the secret and cannot show it again. A lost key is revoked and replaced.
- The last 6 characters are a checksum: the CRC-32 (the zlib one) of the key text before them,
written in base62 (
0-9A-Za-z) and padded with0to 6 characters. Secret scanners use it to tell a real key from a random string without calling TuCOP. A key with a wrong checksum gets401 invalid_api_key, like any other malformed key. - To find keys in your code or logs, match
tcr_(live|test)_(sk|rk|pk)_[A-Za-z0-9_-]{16}\.[A-Za-z0-9_-]{43}[0-9A-Za-z]{6}. Therk(restricted) andpk(publishable) types are reserved and not issued yet.
Older keys
- Keys issued before the checksum format have the form
tcr_<env>_pk_<public id>.<secret>, with no checksum. They keep working until revoked;GET /consumerreports them askind: "pk_legacy". Create anskkey in the console and revoke the old one. - Keys issued before October 2026 have the form
tcr_followed by 64 hex characters. They keep working.GET /consumerreturnsapi_key: nullfor them.
Live and test environments
| Environment | Base URL | Keys accepted |
|---|---|---|
| Production | https://api.ramp.tucop.xyz | tcr_live_sk_... |
| Sandbox | https://sandbox.api.ramp.tucop.xyz | tcr_test_sk_... |
Each environment accepts only its own keys. A test key sent to production, or a live key sent to
the sandbox, gets 401 invalid_api_key with a WWW-Authenticate header. The response does not
say which part was wrong, so check the prefix first.
Expiry
TuCOP can give a key an expiry time. After it, the key gets 401 invalid_api_key, the same as a
revoked key. GET /consumer returns it as api_key.expires_at (null when the key does not
expire). Issue the replacement before that time.
Permissions
Every key works only for the products TuCOP enabled for your consumer. A request for another
product gets 403 product_not_enabled, whatever key you use.
Within those products, a key is either full access or read-only:
| Key | Scopes | Can do |
|---|---|---|
| Full access (default) | Your consumer's scopes | Everything your products allow |
| Read-only | wallet.read, webhook.receive | GET requests only |
A read-only key that sends a POST, PATCH, PUT or DELETE gets
403 api_key_scope_missing, before anything is changed. Use read-only keys for reporting,
reconciliation and monitoring, so a leak of one cannot move money. A read-only key cannot open
party sessions either (POST /sessions/*).
Choose the permissions when you create the key in the console (Llaves or Sandbox,
field Permisos). They cannot be changed afterwards: create a new key and revoke the old one.
GET /consumer returns the scopes the calling key has; a key without wallet.write is
read-only.
IP allowlist
TuCOP can limit a key to IP addresses or ranges (CIDR), such as 203.0.113.0/24. Requests from
any other address get 403 api_key_ip_not_allowed. We recommend it for live keys that can move
money: give TuCOP the egress addresses of your servers.
- The address checked is the public address your request reaches TuCOP from.
- The docs playground sends requests from your browser, so the address checked is your own. The playground only calls the sandbox.
No browser-origin protection
Secret keys (sk) cannot be limited to browser origins. The Origin header is set by whoever
makes the request, so a check on it would not stop anyone holding the key. A key that is in a
browser or a mobile app is public.
Older tcr_<env>_pk_ keys that TuCOP limited to browser origins keep that limit until revoked:
requests from another origin, or without an Origin header, get
403 api_key_origin_not_allowed.
Rate limits
Limits are counted per integrator (your consumer), per key, per wallet, per email or per IP, never shared with other integrators. Every limit is a rolling window. The platform budget of 1,000 requests per minute is shared by all your keys: adding keys does not raise it.
| Limit | Applies to | Budget | Error |
|---|---|---|---|
| Per consumer, platform | All your keys together, on /consumer, /events, /document-types, /sessions, /parties, /destinations, /quotes, /payments, /limits, /rates, /withdrawals and /deposits | 1,000 requests per minute | 429 rate_limited_consumer |
| Per consumer, rates | GET /rates, on top of the platform budget | 120 requests per minute | 429 rate_limited_consumer |
| Per consumer, P2P | All your keys together, on /p2p/* (TuCOP's own apps only) | 1,000 requests per minute | 429 rate_limited_consumer |
| Per consumer, codes | Email codes, wallet nonces and Google nonces issued by /sessions for all your users together | 300 per 15 minutes | 429 rate_limited_consumer |
| Per consumer, sign-in attempts | Code checks and wallet and Google sign-ins on /sessions for all your users together | 1,200 per 15 minutes | 429 rate_limited_consumer |
| Per email | Codes sent to one email address, across all integrators | 3 per 15 minutes | 429 rate_limited_email |
| Per party session | POST /parties with one party session | 10 per 15 minutes | 429 rate_limited_party_session |
| Per key | Keys that TuCOP gave a per-minute limit, counted across all TuCOP servers | Set per key | 429 rate_limited_api_key |
| Per wallet, sessions | POST /sessions/wallet/nonce, per address in the body, across all integrators | 10 nonces per minute | 429 rate_limited_wallet |
| Per wallet, P2P | Wallet-signed P2P endpoints, per X-Wallet-Address (TuCOP's own apps only) | 60 requests per minute | 429 rate_limited_wallet |
| Per IP | Requests without a valid key | 60 requests per minute | 429 rate_limited_ip |
| Per end user, trusted relay | /sessions, per address in X-TuCOPRamp-Client-IP (see below) | 10 email codes per 15 minutes, 10 sign-in attempts per minute, 60 nonces per minute | 429 rate_limited_ip |
If you need a higher budget, write to support@tucop.org with the volume you expect.
Calling from your own server
If your server relays requests for your users, every request reaches TuCOP Ramp from one address.
TuCOP can mark your consumer as a trusted relay. You then send each end user's IP address in the
X-TuCOPRamp-Client-IP header, and per-IP limits count that address instead of your server's:
curl https://api.ramp.tucop.xyz/sessions/email-otp \
--request POST \
--header "X-TuCOPRamp-Key: $TUCOPRAMP_API_KEY" \
--header "X-TuCOPRamp-Client-IP: 203.0.113.7" \
--header "Content-Type: application/json" \
--data '{ "email": "ana@example.com" }'
Send one IPv4 or IPv6 address, the one your server saw the user connect from. Do not forward a
user-supplied X-Forwarded-For as is. With a trusted address, /sessions applies these limits
per end user, besides the per-email and per-consumer ones: 10 email codes per 15 minutes,
10 verify or sign-in attempts per minute and 60 wallet nonces per minute. TuCOP ignores the header
from consumers that are not trusted relays.
Every 429 carries a Retry-After header in seconds. Wait at least that long before the next
request. Poll order status every 15 to 30 seconds rather than faster.
The console's Monitor page shows your own requests per route, with response times (p50, p95,
p99), error rates and how many got a 429, for the last hour up to the last 30 days. The sandbox
has its own (Monitor de sandbox).
Rotation and revocation
You can hold several keys: one per backend, one per environment. To rotate, or before a key expires:
- Create a new key in the console: Llaves for production, Sandbox for test keys.
- Deploy it.
- Revoke the old one in the same page.
Revocation takes effect on the next request. If a key may have leaked, revoke it in the console at once; if you cannot sign in, write to support@tucop.org.