Authentication
TokenCashFlow supports two credential types depending on where your code runs.
Session tokens vs. API keys
| Credential | Header | Use case |
|---|---|---|
| API key | X-API-Key: tcf_live_... | Server-to-server integrations; long-lived; create/revoke in portal |
| Session token | Authorization: Bearer {token} | Client portal SPA; issued on login; expires after inactivity |
For merchant integrations, always use API keys. Session tokens are used by the TokenCashFlow client portal internally and expire after a time of inactivity (default: 24 hours).
API key authentication
Header format
X-API-Key: tcf_live_32randomcharactershereKey lifecycle
- Create a key in the Client Portal → Dashboard → API Keys
- The full key is shown once at creation — copy it immediately
- Keys are stored as SHA-256 hashes; TokenCashFlow cannot recover the original value
- Revoke a key in the portal at any time — takes effect immediately
- Create a new key before revoking an old one to avoid downtime
Key format
tcf_live_{32 URL-safe random characters}Total length: ~44 characters.
KYC requirement
Creating payments and generating API keys require at least Basic KYC. If your KYC has not been approved, the API returns:
json
{
"success": false,
"data": null,
"error": {
"code": "KYC_REQUIRED",
"message": "Basic KYC approval is required for this operation.",
"details": {}
}
}HTTP status: 403 Forbidden
Complete KYC in the Client Portal → Dashboard → Identity Verification.
Rate limits
| Scope | Default limit | Response on exceeded |
|---|---|---|
| Per API key | 60 requests/second | 429 Too Many Requests |
| Per user (all keys combined) | 60 requests/second | 429 Too Many Requests |
| Global platform | 1,000 requests/second | 429 Too Many Requests |
On rate-limit hit, the response includes a Retry-After header with the number of seconds to wait.
Recommended back-off strategy:
python
import time, random
def request_with_backoff(fn, max_retries=5):
for attempt in range(max_retries):
response = fn()
if response.status_code != 429:
return response
retry_after = int(response.headers.get("Retry-After", 1))
jitter = random.uniform(0, 0.5)
time.sleep(retry_after + jitter)
raise Exception("Rate limit retries exhausted")Security best practices
- Never expose your API key in frontend code (JavaScript, HTML, mobile app bundles). API keys are for server-side use only.
- Store keys as environment variables, not in source code.
- Rotate keys immediately if you suspect compromise: create a new key, deploy it, then revoke the old one.
- Use a separate key per environment (staging, production).
- Monitor
last_used_atin the portal to detect unexpected key usage.

