Skip to main content

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

HeaderTypeRequiredDescription
x-webhook-signaturestringConditionalHMAC SHA-256 signature (required if PAYMENTS_WEBHOOK_SECRET is set)
Content-TypestringYesMust 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 TypeDescription
payment_intent.completedPayment Intent succeeded
payment_intent.processingPayment Intent is processing
payment_intent.canceledPayment Intent was canceled
payment_intent.requires_payment_methodPayment Intent needs payment method

Signature Verification

If PAYMENTS_WEBHOOK_SECRET is configured, verify the signature:

  1. Get the signature from x-webhook-signature header
  2. Compute HMAC SHA-256 of the request body using your secret
  3. 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

  1. Respond quickly - Return 200 OK within 10 seconds (timeout is 10 seconds)
  2. Process asynchronously - Handle events in background jobs
  3. Verify signatures - Always verify webhook signatures
  4. Make handlers idempotent - Handle duplicate events gracefully
  5. 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

ParameterTypeRequiredDescription
apiKeyIdnumberYesAPI 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

ParameterTypeRequiredDescription
apiKeyIdnumberYesAPI 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

ParameterTypeDefaultDescription
limitnumber50Number of logs to return
offsetnumber0Number 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

FieldTypeDescription
idnumberUnique log identifier
apiKeyIdnumberAPI key ID that triggered the webhook
userIdnumberUser ID who owns the API key
eventstringEvent type (e.g. payment_intent.completed)
urlstringWebhook URL that was called
statusCodenumberHTTP status code returned by webhook endpoint
successbooleanWhether the webhook was delivered successfully
attemptNumbernumberRetry attempt number (1, 2, or 3)
errorstringError message if delivery failed
responsestringResponse body from webhook endpoint
createdAtstringISO 8601 timestamp when log was created

See Also