Developer Documentation
The Tribe Pay Merchant API lets you accept payments from your website or application. You initiate a payment session from your server, redirect your customer to a hosted checkout page, and receive the result via webhook or status query.
Card data is collected by the payment gateway directly — it never passes through Tribe Pay's servers or yours, satisfying PCI DSS SAQ-A.
Base URL
https://api.tribe-pay.com
All API requests must be made over HTTPS. All request and response bodies are JSON.
During development, replace https://api.tribe-pay.com with your local instance URL (e.g. http://127.0.0.1:8123) and use a pk_test_ key to avoid charging real cards.
Quick Start
Get a payment running in three steps.
Log in to the Tribe Pay merchant portal, go to Developer → API Keys, and copy your live key (pk_live_…) or test key (pk_test_…).
Send the order details and receive a checkout_url. Redirect your customer to that URL — Tribe Pay handles the card form.
After payment, the customer is sent to your return_url. Confirm the outcome by calling GET /api/pay/status/{order_id} or by listening for the payment.completed webhook event.
Authentication
All payment API requests require two headers: a public API key and a secret key.
X-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-Secret-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-API-Key identifies your merchant account. X-Secret-Key proves the request is from you. Both are required on every request.
Key types
| Prefix | Mode | Behaviour |
|---|---|---|
| pk_live_… | live | Real money is charged. Payments are credited to your wallet. |
| pk_test_… | sandbox | No real money. Use test cards. Wallets are not credited. |
Keep both keys secret. Never expose them in client-side code, mobile apps, or public repositories. If a key pair is compromised, revoke it immediately from the merchant portal and generate a new one. The secret key is shown only once — save it securely.
You can have up to 5 active keys at once. Label each key by environment or service (e.g. "Production Server", "Staging") for easy management.
Sandbox Mode
Use a pk_test_ key to run payments in sandbox mode. All requests hit real API endpoints — the only difference is that the payment gateway operates in test mode and no real funds move.
| Behaviour | Live | Sandbox |
|---|---|---|
| Real money charged | Yes | No |
| Wallet credited on success | Yes | No |
| Webhooks fired | Yes | Yes |
Response mode field | live | sandbox |
See Test Cards for card numbers to use on the sandbox checkout page.
Initiate Payment
Creates a checkout session and returns a hosted checkout URL. Call this from your server when a customer is ready to pay.
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| order_id | string | Required | Your unique order identifier. Max 100 characters. Must be unique per merchant. |
| amount | number | Required | Payment amount as a decimal. Minimum 1, maximum 1,000,000. |
| currency | string | Required | ISO 4217 currency code (e.g. USD, EUR, ARS). Must match the currency your gateway account supports. |
| customer_name | string | Optional | Customer's full name. Shown on the checkout page. |
| customer_email | string | Optional | Customer's email address. Used for payment receipts. |
| customer_phone | string | Optional | Customer's phone number including country code. |
| description | string | Optional | Short description of the purchase. Shown on checkout. Max 255 characters. |
| return_url | string | Required | URL where the customer is sent after payment (success or failure). Must be HTTPS in production. |
| cancel_url | string | Optional | URL when the customer cancels. Falls back to return_url if not provided. |
| webhook_url | string | Optional | One-time webhook URL for this payment only. Overrides any webhook configured in the portal for this request. |
Example request
curl -X POST https://api.tribe-pay.com/api/pay/initiate \
-H "X-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Secret-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"order_id": "ORD-2024-001",
"amount": 99.00,
"currency": "USD",
"customer_name": "John Smith",
"customer_email": "john@example.com",
"customer_phone": "+1 555 000 1234",
"description": "Premium Plan — 1 Month",
"return_url": "https://yoursite.com/payment/return",
"cancel_url": "https://yoursite.com/payment/cancel",
"webhook_url": "https://yoursite.com/webhooks/tribepay"
}'<?php
$payload = [
'order_id' => 'ORD-2024-001',
'amount' => 99.00,
'currency' => 'USD',
'customer_name' => 'John Smith',
'customer_email' => 'john@example.com',
'customer_phone' => '+1 555 000 1234',
'description' => 'Premium Plan — 1 Month',
'return_url' => 'https://yoursite.com/payment/return',
'cancel_url' => 'https://yoursite.com/payment/cancel',
];
$ch = curl_init('https://api.tribe-pay.com/api/pay/initiate');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'X-Secret-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($payload),
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
header('Location: ' . $data['checkout_url']);
exit;const axios = require('axios');
const { data } = await axios.post(
'https://api.tribe-pay.com/api/pay/initiate',
{
order_id: 'ORD-2024-001',
amount: 99.00,
currency: 'USD',
customer_name: 'John Smith',
customer_email: 'john@example.com',
customer_phone: '+1 555 000 1234',
description: 'Premium Plan — 1 Month',
return_url: 'https://yoursite.com/payment/return',
cancel_url: 'https://yoursite.com/payment/cancel',
},
{ headers: { 'X-API-Key': 'pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Secret-Key': 'sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' } }
);
res.redirect(data.checkout_url);import requests
response = requests.post(
'https://api.tribe-pay.com/api/pay/initiate',
json={
'order_id': 'ORD-2024-001',
'amount': 99.00,
'currency': 'USD',
'customer_name': 'John Smith',
'customer_email': 'john@example.com',
'customer_phone': '+1 555 000 1234',
'description': 'Premium Plan — 1 Month',
'return_url': 'https://yoursite.com/payment/return',
'cancel_url': 'https://yoursite.com/payment/cancel',
},
headers={'X-API-Key': 'pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Secret-Key': 'sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}
)
data = response.json()
return redirect(data['checkout_url'])Response 201 Created
{
"success": true,
"order_id": "ORD-2024-001",
"reference": "TRP-K7XB-NQ2A",
"amount": 99.00,
"currency": "USD",
"mode": "live",
"checkout_url": "https://api.tribe-pay.com/checkout/a8f3c2e1d0b4...",
"expires_at": "2024-07-22T15:30:00+00:00"
}| Field | Type | Description |
|---|---|---|
| reference | string | Tribe Pay's unique transaction reference (TRP-XXXX-XXXX). Store this alongside your order ID. |
| mode | string | live or sandbox depending on which key was used. |
| checkout_url | string | Redirect your customer here. The session expires in 30 minutes. |
| expires_at | string | ISO 8601 timestamp when the checkout URL expires. |
The checkout_url expires in 30 minutes. If the customer does not complete payment in time, the status becomes expired. Create a new session to let them retry.
Handle Return URL
After the customer pays (or cancels), Tribe Pay redirects them to your return_url with the following query parameters appended.
https://yoursite.com/payment/return
?status=completed
&order_id=ORD-2024-001
&reference=TRP-K7XB-NQ2A| Parameter | Values |
|---|---|
| status | completed · failed · cancelled · expired |
| order_id | Your original order ID. |
| reference | Tribe Pay reference code (TRP-XXXX-XXXX). |
Do not rely on return URL parameters alone to confirm payment. These can be manipulated in the browser. Always verify by calling GET /api/pay/status/{order_id} from your server or by processing a signed webhook event.
Recommended pattern
<?php
$orderId = $_GET['order_id'] ?? '';
$ch = curl_init("https://api.tribe-pay.com/api/pay/status/{$orderId}");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['X-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Secret-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
if ($data['status'] === 'completed') {
fulfil_order($orderId);
show_success_page();
} else {
show_failure_page($data['status']);
}Check Payment Status
Query the current status of any payment by its order_id.
curl https://api.tribe-pay.com/api/pay/status/ORD-2024-001 \
-H "X-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Secret-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"<?php
$ch = curl_init('https://api.tribe-pay.com/api/pay/status/ORD-2024-001');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['X-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Secret-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $data['status'];const { data } = await axios.get(
'https://api.tribe-pay.com/api/pay/status/ORD-2024-001',
{ headers: { 'X-API-Key': 'pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Secret-Key': 'sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' } }
);
console.log(data.status);response = requests.get(
'https://api.tribe-pay.com/api/pay/status/ORD-2024-001',
headers={'X-API-Key': 'pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'X-Secret-Key': 'sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}
)
print(response.json()['status'])Response 200 OK
{
"success": true,
"order_id": "ORD-2024-001",
"reference": "TRP-K7XB-NQ2A",
"amount": 99.00,
"currency": "USD",
"status": "completed",
"mode": "live",
"paid_at": "2024-07-22T14:35:22+00:00"
}Webhooks
Webhooks are server-to-server notifications sent by Tribe Pay when a payment event occurs. They are the most reliable way to fulfil orders — unlike the return URL, webhooks are delivered even if the customer closes their browser.
Setup
Add a webhook endpoint in the merchant portal under Developer → Webhooks. Specify your URL and choose which events to receive. You'll receive a signing secret — save it, it's shown only once.
Your webhook endpoint must respond with HTTP 200 within 10 seconds. Acknowledge receipt first, then process the event asynchronously.
Events
Subscribe to * to receive all events now and in the future.
Payload format
{
"event": "payment.completed",
"timestamp": "2024-07-22T14:35:22+00:00",
"data": {
"order_id": "ORD-2024-001",
"reference": "TRP-K7XB-NQ2A",
"amount": 99.00,
"currency": "USD",
"status": "completed",
"mode": "live",
"paid_at": "2024-07-22T14:35:22+00:00"
}
}Verifying the signature
Every webhook includes an X-TribePay-Signature header. Verify it before processing to confirm the request came from Tribe Pay.
Always verify the signature. Anyone who knows your webhook URL can send fake events. Reject requests with a missing or invalid signature with 401.
<?php
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_TRIBEPAY_SIGNATURE'] ?? '';
$signingSecret = getenv('TRIBE_WEBHOOK_SECRET');
$expected = hash_hmac('sha256', $rawBody, $signingSecret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('Unauthorized');
}
$event = json_decode($rawBody, true);
http_response_code(200);
echo 'OK';
ob_flush(); flush();
if ($event['event'] === 'payment.completed') {
fulfil_order($event['data']['order_id']);
}
if ($event['event'] === 'payment.refunded') {
reverse_fulfilment($event['data']['order_id']);
}const crypto = require('crypto');
app.post('/webhooks/tribepay',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.headers['x-tribepay-signature'] ?? '';
const signingSecret = process.env.TRIBE_WEBHOOK_SECRET;
const expected = crypto
.createHmac('sha256', signingSecret)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
)) return res.status(401).send('Unauthorized');
res.status(200).send('OK');
const event = JSON.parse(req.body);
if (event.event === 'payment.completed') {
fulfilOrder(event.data.order_id);
}
}
);import hmac, hashlib, json, os
from flask import Flask, request, abort
SIGNING_SECRET = os.environ['TRIBE_WEBHOOK_SECRET']
@app.route('/webhooks/tribepay', methods=['POST'])
def tribepay_webhook():
signature = request.headers.get('X-TribePay-Signature', '')
raw_body = request.get_data()
expected = hmac.new(
SIGNING_SECRET.encode(), raw_body, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature):
abort(401)
event = json.loads(raw_body)
if event['event'] == 'payment.completed':
fulfil_order(event['data']['order_id'])
return 'OK', 200Retry policy
If your endpoint does not respond with 200 within 10 seconds, Tribe Pay retries with exponential back-off:
| Attempt | Delay |
|---|---|
| 1st retry | 5 minutes |
| 2nd retry | 30 minutes |
| 3rd retry | 2 hours |
| 4th retry | 8 hours |
| 5th retry | 24 hours — final attempt |
Your handler may receive the same event more than once due to retries. Make order fulfilment idempotent — check if an order is already fulfilled before acting on a webhook.
Payment Statuses
| Status | Description | Terminal? |
|---|---|---|
| pending | Checkout session created. Customer has not yet reached the payment page. | No |
| processing | Customer redirected to the payment gateway. Awaiting confirmation. | No |
| completed | Payment captured successfully. Funds will be credited to your Tribe Pay wallet. | Yes |
| failed | Payment was declined, rejected by the bank, or an error occurred at the gateway. | Yes |
| expired | The 30-minute checkout window elapsed before the customer completed payment. | Yes |
| refunded | A completed payment was reversed. The amount is deducted from your wallet. | Yes |
Error Codes
Errors return a JSON object with a human-readable message. Validation errors also include an errors object.
{
"message": "The given data was invalid.",
"errors": {
"amount": ["The amount field is required."],
"return_url": ["The return url must be a valid URL."]
}
}HTTP status codes
X-API-Key / X-Secret-Key. Both headers are required and must match an active key pair.errors object for field-level details.Common errors & fixes
| Error message | Fix |
|---|---|
| No active payment gateway assigned | Ask your Tribe Pay admin to assign a gateway account to your merchant profile. |
| The order id has already been taken | Your order_id is not unique. Each payment must use a different value. |
| The return url field is required | Include a valid absolute URL in the return_url field. |
| Unauthenticated | The X-API-Key or X-Secret-Key header is missing, invalid, or the key pair has been revoked. |
Test Cards
Use these card numbers on the sandbox checkout page (any future expiry date, any CVV unless specified).
Cards that complete successfully
| Brand | Number | CVV | Expiry |
|---|---|---|---|
| Visa | 4200 0000 0000 0000 | Any 3 digits | Any future date |
| Visa | 4000 0000 0000 0051 | 745 | Any future date |
| Mastercard | 5101 0821 8725 6503 | 123 | Any future date |
| Mastercard | 5454 5454 5454 5454 | Any 3 digits | Any future date |
| Amex | 3755 1051 3169 537 | 123 | Any future date |
3D Secure test cards
| Brand | Number | CVV | Behaviour |
|---|---|---|---|
| Visa (3DS) | 4000 0000 0000 0002 | 237 | Triggers 3D Secure challenge, then completes |
Test cards only work with a pk_test_ API key. Using a test card number with a live key will result in a real declined charge.
Questions or issues? Contact your Tribe Pay account manager or open a support ticket from the merchant portal.
API Test Console
An interactive browser-based tool for testing the Tribe Pay Merchant API — without writing any code. The console shows a live request preview, generates a ready-to-run curl command, sends the request, and displays the response with syntax highlighting.
Server-to-Server (S2S) API
The S2S API allows your server to submit a card payment in a single HTTP call — no hosted checkout page is involved. Your backend collects the card number, sends it directly to Tribe Pay, and receives an immediate response telling you whether the payment completed, requires 3DS authentication, or failed.
PCI DSS scope applies. Because your server handles raw card numbers you must be PCI DSS SAQ-D compliant (or use a certified vault/tokenisation service). Contact your acquirer or QSA before going live.
Hosted checkout vs S2S
Hosted Checkout (default)
- Card data never touches your server
- PCI DSS SAQ-A — minimal compliance burden
- Tribe Pay renders the payment form
- Customer is redirected then returned via
return_url
S2S API (this section)
- Your server collects and sends card data
- Full control over the UI and checkout flow
- Requires IP whitelisting + PCI compliance
- Response includes a
redirect_urlwhen 3DS is triggered
POST /api/s2s/charge
Submit a card payment from your server. The gateway is called synchronously and the response includes the final status or a 3DS redirect URL.
/api/s2s/charge
Uses the same X-API-Key / X-Secret-Key headers as the hosted checkout API. In addition, your server's IP address must be in the merchant's S2S whitelist — requests from unlisted IPs receive 403 Forbidden.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| order_id | string | required | Your unique order reference (max 100 chars). Must be different for every payment. |
| amount | number | required | Payment amount in the minor currency unit (e.g. 1500 = ₹1,500 or $15.00). Min 1, max 1,000,000. |
| currency | string | required | ISO 4217 currency code, e.g. INR, USD. |
| return_url | string (URL) | required | Where the customer is sent after 3DS. Must be an absolute HTTPS URL. |
| customer_name | string | required | Full name of the customer, max 150 chars. |
| customer_email | string | required | Customer email address. Passed to the gateway for fraud scoring. |
| customer_phone | string | optional | Customer phone number. Digits only recommended. |
| description | string | optional | Human-readable order description, max 255 chars. |
| webhook_url | string (URL) | optional | Override the merchant's default webhook URL for this transaction. |
| cancel_url | string (URL) | optional | Where to send the customer if they cancel at the 3DS page. |
card object
| Field | Type | Required | Description |
|---|---|---|---|
| card.number | string | required | Card number, 13–19 digits. Spaces are stripped automatically. |
| card.expiry_month | string | required | 2-digit month, e.g. "09". |
| card.expiry_year | string | required | 4-digit year, e.g. "2028". Must be 4 digits — 2-digit years are rejected. |
| card.cvv | string | required | 3 or 4 digit security code. Never logged. |
| card.holder_name | string | required | Cardholder name exactly as it appears on the card. |
| card.country | string | optional* | 2-letter ISO 3166-1 country code, e.g. "IN", "US". Required for Paynectra. |
| card.postcode | string | optional* | Billing postcode / ZIP code. Required for Paynectra. |
| card.city | string | optional* | Billing city. Required for Paynectra. |
| card.address | string | optional* | Street address. Required for Paynectra. |
| card.state | string | optional | State or province code, e.g. "MH". |
| card.phone | string | optional | Billing contact phone. Digits only, up to 20 chars. |
* Fields marked optional* are required when the merchant's active gateway is Paynectra.
Example request
curl -X POST https://api.tribe-pay.com/api/s2s/charge \
-H "X-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Secret-Key: sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"order_id": "ORDER-20240917-001",
"amount": 1500,
"currency": "INR",
"return_url": "https://yoursite.com/payment/return",
"customer_name": "Rahul Sharma",
"customer_email": "rahul@example.com",
"customer_phone": "9876543210",
"card": {
"number": "4111111111111111",
"expiry_month": "09",
"expiry_year": "2028",
"cvv": "123",
"holder_name": "RAHUL SHARMA",
"country": "IN",
"postcode": "400001",
"city": "Mumbai",
"address": "12 MG Road",
"state": "MH",
"phone": "9876543210"
}
}'
S2S Response Types
The status field in every response tells you what action to take next.
| Status | HTTP | Meaning & next step |
|---|---|---|
| completed | 200 | Payment captured. A paid_at timestamp is included. Show a success screen immediately. |
| pending_3ds | 200 | Card requires 3DS authentication. Redirect the customer to redirect_url. After auth the customer returns to your return_url. Poll GET /api/pay/status/{order_id} or wait for the webhook. |
| processing | 200 | Gateway accepted the charge but has not yet confirmed. Poll /api/pay/status or wait for the webhook. |
| failed | 422 | Card declined by the gateway. Show an appropriate error to the customer and allow retry. |
| invalid | 422 | Input validation failed (missing/invalid field). The message field explains what is wrong. No payment record was created. |
Successful completion
{
"success": true,
"status": "completed",
"order_id": "ORDER-20240917-001",
"reference": "TRB-20240917-A1B2C3",
"amount": 1500,
"currency": "INR",
"mode": "live",
"paid_at": "2024-09-17T10:32:44+05:30"
}
3DS redirect required
{
"success": true,
"status": "pending_3ds",
"order_id": "ORDER-20240917-001",
"reference": "TRB-20240917-A1B2C3",
"amount": 1500,
"currency": "INR",
"mode": "live",
"redirect_url": "https://gateway.paynectra.com/3ds/xxxxxxxx",
"message": "Card requires 3DS authentication. Redirect customer to redirect_url to complete payment."
}
Declined / invalid
{
"success": false,
"status": "failed",
"order_id": "ORDER-20240917-001",
"reference": "TRB-20240917-A1B2C3",
"amount": 1500,
"currency": "INR",
"mode": "live",
"message": "Payment declined by the gateway. Please check card details and try again."
}
IP Whitelist Setup
The S2S endpoint accepts requests only from server IPs you have explicitly whitelisted. Any request from an unlisted IP receives 403 Forbidden, even with valid credentials.
Whitelisting is per-merchant. You can list individual IPv4/IPv6 addresses or CIDR ranges (e.g. 203.0.113.0/24). Changes take effect immediately.
How to add IPs
Go to Merchants → [your merchant] → S2S API Access.
Enter one IP address or CIDR block per line in the text area that appears.
The list is updated immediately. Test with tribe-s2s-test.php or a curl command from your server to verify access.
Never whitelist 0.0.0.0/0. S2S carries raw card data; allowing any IP defeats the access control entirely. If you need broader access, use the hosted checkout API instead.
S2S error responses
X-API-Key / X-Secret-Key headers.message explains which field. Status is invalid and no payment record is created.failed. A payment record exists in failed state.