Authentication

How to authenticate with the DUAL API using JWT tokens and API keys.

Authentication Methods

The DUAL API supports two authentication methods in v3.0.0+. Both are valid and can be used independently:

Obtain a JWT token using the OTP flow. First, request an OTP:

curl -X POST https://gateway-48587430648.europe-west6.run.app/auth/otp \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com"}'

Then authenticate with the OTP:

curl -X POST https://gateway-48587430648.europe-west6.run.app/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "otp": "123456"
  }'

// Response:
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_in": 3600
}

Use the token in subsequent requests:

curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  https://gateway-48587430648.europe-west6.run.app/wallets/me

2. API Key (Via x-api-key Header)

Create an API key via POST /api-keys and include it in the x-api-key header:

curl -X POST https://gateway-48587430648.europe-west6.run.app/api-keys \
  -H "Authorization: Bearer $JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "name": "My API Key" }'

// Response:
{
  "id": "key_abc123xyz",
  "key": "sk_live_1a2b3c4d5e6f7g8h",
  "created_at": "2026-03-17T10:00:00Z"
}

Use the API key in subsequent requests:

curl -H "x-api-key: sk_live_1a2b3c4d5e6f7g8h" \
  https://gateway-48587430648.europe-west6.run.app/wallets/me

Rate Limits

API requests are rate-limited per organization. Limits vary by operation type: 300 req/min for reads, 60 req/min for writes, and 2,000 actions/sec for Event Bus operations. See the Rate Limits page for the full breakdown.

Rate limit status is included in every response via these headers:

  • X-RateLimit-Limit — Maximum requests per minute for this endpoint group
  • X-RateLimit-Remaining — Requests remaining in current window
  • X-RateLimit-Reset — Unix timestamp when the window resets

When you exceed the limit, the API returns 429 Too Many Requests with a Retry-After header (in seconds):

curl https://gateway-48587430648.europe-west6.run.app/objects \
  -H "x-api-key: $DUAL_API_KEY"

// Response (429):
{
  "error": "rate_limit_exceeded",
  "message": "Write rate limit of 60 req/min per org exceeded",
  "retry_after": 30
}

// Headers:
Retry-After: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1710758430

Contact support to request higher rate limits.

Security Model

DUAL implements a 5-layer security architecture:

  • Layer 1 — EIP-712 typed data signatures for all state-changing requests
  • Layer 2 — Hash chain integrity for action ordering
  • Layer 3 — Batch fingerprinting for on-chain anchoring
  • Layer 4 — ZK proof verification for dispute resolution
  • Layer 5 — Smart contract enforcement on Ethereum