Webhooks API Reference
Complete reference for the Webhooks API endpoint.
Base URL
https://api.orcarail.com/api/v1
Receive Webhook
Receive webhook events from OrcaRail.
POST /webhooks
Authentication
- API Key (Basic Auth) - Optional, but recommended
Headers
| Header | Type | Required | Description |
|---|---|---|---|
x-webhook-signature | string | Conditional | HMAC SHA-256 signature (required if PAYMENTS_WEBHOOK_SECRET is set) |
Content-Type | string | Yes | Must be application/json |
Request Body
{
"type": "payment_intent.completed",
"data": {
"object": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"amount": "100.00",
"currency": "usd",
"status": "succeeded",
"metadata": {
"order_id": "12345"
}
}
}
}
Response
Always return 200 OK immediately:
{
"received": true
}
Webhook Events
| Event Type | Description |
|---|---|
payment_intent.completed | Payment Intent succeeded |
payment_intent.processing | Payment Intent is processing |
payment_intent.canceled | Payment Intent was canceled |
payment_intent.requires_payment_method | Payment Intent needs payment method |
Signature Verification
If PAYMENTS_WEBHOOK_SECRET is configured, verify the signature:
- Get the signature from
x-webhook-signatureheader - Compute HMAC SHA-256 of the request body using your secret
- Compare signatures using timing-safe comparison
Example: Node.js
const crypto = require('crypto')
function verifySignature(receivedSignature, payload, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex')
const receivedBuffer = Buffer.from(receivedSignature)
const expectedBuffer = Buffer.from(expectedSignature)
if (receivedBuffer.length !== expectedBuffer.length) {
return false
}
return crypto.timingSafeEqual(receivedBuffer, expectedBuffer)
}
Response Requirements
Success Response
Always return 200 OK with {"received": true} immediately, even if processing fails:
{
"received": true
}
Error Response
If signature verification fails, return 400 Bad Request:
{
"statusCode": 400,
"message": "Invalid webhook signature",
"error": "Bad Request"
}
Retry Logic
If your endpoint returns a non-200 status code or fails to respond, OrcaRail will retry the webhook:
- Maximum attempts: 3 attempts (1 initial + 2 retries)
- Backoff strategy: Exponential backoff starting at 2 seconds
- Timeout: 10 seconds per request
Best Practices
- Respond quickly - Return
200 OKwithin 10 seconds (timeout is 10 seconds) - Process asynchronously - Handle events in background jobs
- Verify signatures - Always verify webhook signatures
- Make handlers idempotent - Handle duplicate events gracefully
- Log events - Log all received events for debugging
Testing
Test your webhook endpoint:
curl -X POST https://your-webhook-url.com/webhooks/orcarail \
-H "Content-Type: application/json" \
-H "x-webhook-signature: test-signature" \
-d '{
"type": "payment_intent.completed",
"data": {
"object": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"amount": "100.00",
"currency": "usd",
"status": "succeeded"
}
}
}'
Test Webhook
Send a test webhook event to your configured webhook URL.
POST /webhooks/test/:apiKeyId
Authentication
- Bearer Token (JWT) - Required
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
apiKeyId | number | Yes | API key ID |
Response
{
"success": true
}
Error Responses
404 Not Found - API key not found or webhook URL not configured:
{
"statusCode": 404,
"message": "Webhook URL not configured for this API key",
"error": "Not Found"
}
Send Custom Webhook
Send a custom webhook payload to your configured webhook URL.
POST /webhooks/test/:apiKeyId/custom
Authentication
- Bearer Token (JWT) - Required
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
apiKeyId | number | Yes | API key ID |
Request Body
{
"payload": {
"type": "payment_intent.completed",
"data": {
"object": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"amount": "100.00",
"currency": "usd",
"status": "succeeded"
}
}
}
}
Response
{
"success": true
}
Get Webhook Logs
Retrieve webhook delivery logs for your account.
GET /webhooks/logs
Authentication
- Bearer Token (JWT) - Required
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Number of logs to return |
offset | number | 0 | Number of logs to skip |
Response
{
"logs": [
{
"id": 1,
"apiKeyId": 1,
"userId": 1,
"event": "payment_intent.completed",
"url": "https://api.example.com/webhooks/orcarail",
"statusCode": 200,
"success": true,
"attemptNumber": 1,
"createdAt": "2024-01-01T00:00:00.000Z"
}
],
"total": 1
}
Log Object Fields
| Field | Type | Description |
|---|---|---|
id | number | Unique log identifier |
apiKeyId | number | API key ID that triggered the webhook |
userId | number | User ID who owns the API key |
event | string | Event type (e.g. payment_intent.completed) |
url | string | Webhook URL that was called |
statusCode | number | HTTP status code returned by webhook endpoint |
success | boolean | Whether the webhook was delivered successfully |
attemptNumber | number | Retry attempt number (1, 2, or 3) |
error | string | Error message if delivery failed |
response | string | Response body from webhook endpoint |
createdAt | string | ISO 8601 timestamp when log was created |
See Also
- Webhooks Guide - Detailed webhook documentation
- Webhook Configuration - Configure webhooks for API keys
- Webhook Events - All available events
- Webhook Signatures - Signature verification