Authentication
The ChainPal API uses API keys for authentication. All API requests must include a valid API key in the Authorization header.
API Key Types
ChainPal provides two types of API keys for different use cases:
Public Keys
- Prefix:
cp_pk_test_ or cp_pk_live_
- Use Case: Client-side integrations (e.g., initializing payments from your frontend)
- Permissions: Limited to creating payments and getting quotes
- Endpoints:
POST /payments, POST /payments/quote
Secret Keys
- Prefix:
cp_sk_test_ or cp_sk_live_
- Use Case: Server-side integrations only
- Permissions: Full API access including payment verification and listing
- Endpoints: All endpoints, including
GET /payments, GET /payments/:paymentId, GET /payments/:paymentId/verify
Never expose your Secret Keys in client-side code. Secret keys should only
be used in server-to-server communications.
Key Prefixes
The prefix encodes both the type (public vs secret) and the
environment (test vs live):
| Environment | Key Type | Prefix |
|---|
| Test | Public | cp_pk_test_ |
| Test | Secret | cp_sk_test_ |
| Live | Public | cp_pk_live_ |
| Live | Secret | cp_sk_live_ |
Test and Live keys are independent — use them side-by-side
There is no per-account “active environment” toggle. Each request is
routed by the prefix on the key you send:
cp_*_test_* → request is processed in test mode
cp_*_live_* → request is processed in live mode
This means you can run a staging integration against your test keys at
the same time your production servers are calling with live keys —
without flipping any switch.
What this implies for your integration:
- The
environment field on every payment object reflects the env of
the key used to create it. A payment created with a test key is and
stays a test payment; same for live.
- Webhook URLs and callback URLs are configured per environment
(
testWebhookURL + testCallbackURL, liveWebhookURL +
liveCallbackURL). The dispatcher picks the right pair based on the
payment’s env.
- Rate limits are tracked per
(business, environment) bucket — test
traffic doesn’t eat into your live budget.
Migrating from the old “switch environment” model? The
POST /users/public-api/toggle-environment endpoint is deprecated and
returns 410 Gone. Just send requests with whichever key matches the
env you want.
Include your API key in the Authorization header using the Bearer scheme:
Authorization: Bearer cp_pk_test_your_api_key
Example Requests
Using a Public Key (Initialize Payment)
curl -X POST https://api.chainpal.org/api/v1/payments \
-H "Authorization: Bearer cp_pk_test_abc123" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"customerEmail": "customer@example.com"
}'
Using a Secret Key (Verify Payment)
curl https://api.chainpal.org/api/v1/payments/507f1f77bcf86cd799439011/verify \
-H "Authorization: Bearer cp_sk_test_xyz789"
IP Whitelisting
For enhanced security, you can configure IP whitelisting in your dashboard. When enabled:
- Only requests from whitelisted IP addresses will be accepted for Secret Key endpoints
- Requests from non-whitelisted IPs will receive a
403 Forbidden response
- You can whitelist up to 3 IP addresses
IP whitelisting applies to the following endpoints:
GET /payments/:paymentId/verify
GET /payments/reference/:reference/verify
GET /payments/:paymentId
GET /payments
Rate Limiting
API requests are rate-limited to prevent abuse:
| Key Type | Default Rate Limit |
|---|
| Public Key | 30 requests per minute |
| Secret Key | 100 requests per minute |
When you exceed the rate limit, you’ll receive a 429 Too Many Requests response.
Authentication Errors
| Status Code | Message | Description |
|---|
401 | api key is invalid | The provided API key is not valid |
401 | api key not provided | No Authorization header was sent |
403 | access denied | IP address not in whitelist |