Inyo

Webhooks

Webhooks allow you to receive real-time notifications when important events occur in the Inyo platform — such as a transaction changing status from Pending to Paid, or a document verification completing. Instead of polling the API, you register a callback URL and Inyo pushes events to you.


Supported Events

EventDescriptionWhen It Fires
TransactionStatusChangedA transaction's payout status has changedStatus transitions (e.g., Pending → Paid, Pending → Cancelled)
documentUpdatedEventsA document verification status has been updatedAfter compliance team reviews an uploaded document
agentStatusChangedEventAn agent's status or configuration has changedAgent approval, suspension, or configuration updates
TransactionComplianceStatusChangedA transaction's compliance status has changedStatus transitions (e.g., Hold → Approved, Hold → Rejected)

Registering a Webhook

Endpoint: POST /organizations/{tenant}/webhooks
Authentication: Tenant-level (x-api-key)

curl --request POST \
  --url https://{FQDN}/organizations/$TENANT/webhooks \
  --header 'Content-Type: application/json' \
  --header "x-api-key: $API_KEY" \
  --data '{
  "events": [
    "transactionStatusChanged",
    "documentUpdatedEvents",
    "agentUpdatedEvents"
  ],
  "url": "https://your-server.com/api/webhooks/inyo"
}'

💡 You can register for all events or only the ones relevant to your integration.


Listing Registered Webhooks

Endpoint: GET /organizations/{tenant}/webhooks
Authentication: Tenant-level (x-api-key)

curl --request GET \
  --url https://{FQDN}/organizations/$TENANT/webhooks \
  --header "x-api-key: $API_KEY"

Deleting a Webhook

Endpoint: DELETE /organizations/{tenant}/webhooks/{webhookId}
Authentication: Tenant-level (x-api-key)

curl --request DELETE \
  --url https://{FQDN}/organizations/$TENANT/webhooks/$WEBHOOK_ID \
  --header "x-api-key: $API_KEY"

Webhook Payload

When an event occurs, Inyo sends an HTTP POST request to your registered URL with a JSON payload. Your endpoint should:

  1. Accept POST requests with Content-Type: application/json
  2. Return an HTTP 2xx response to acknowledge receipt
  3. Process the event asynchronously (don't block the response)

Example: Transaction Status Changed

{
  "event": "transactionStatusChanged",
  "transactionId": "9035ee18-052b-4176-a940-ccfe599a1829",
  "tenantId": "your_tenant",
  "complianceStatus": "Approved",
  "payoutStatus": "Paid",
  "externalId": "GMT000648512199",
  "timestamp": "2025-11-21T14:48:05.891186Z",
  "messages": [
    {
      "message": "STANDARD - PAYMENT_DELIVERED",
      "createdBy": "system",
      "type": "SystemUpdate"
    }
  ]
}

Best Practices

Idempotency

Your webhook handler should be idempotent — the same event may be delivered more than once. Use the transactionId and event data to deduplicate.

Respond Quickly

Return a 200 OK as soon as you receive the webhook. Perform heavy processing (database updates, notifications, etc.) asynchronously. If your endpoint takes too long to respond, the delivery may be retried.

Secure Your Endpoint

  • Use HTTPS — always register an https:// URL for your webhook endpoint.
  • Validate the source — consider IP allowlisting or implementing shared-secret verification if supported by your integration agreement.
  • Don't trust the payload blindly — after receiving a transactionStatusChanged event, fetch the transaction via GET /fx/transactions/{id} to confirm the current status before taking action.

Handle Failures Gracefully

If your endpoint returns a non-2xx response or is unreachable, Inyo will retry delivery. Design your system to handle:

  • Duplicate deliveries (idempotency)
  • Out-of-order deliveries (check timestamps)
  • Delayed deliveries (don't assume real-time)

Integration Example

Here's a minimal Node.js webhook handler:

const express = require('express');
const app = express();
app.use(express.json());

app.post('/api/webhooks/inyo', async (req, res) => {
  // Acknowledge immediately
  res.status(200).send('OK');

  const { event, transactionId, complianceStatus, payoutStatus } = req.body;

  console.log(`[Webhook] ${event} — Transaction: ${transactionId}`);
  console.log(`  Compliance: ${complianceStatus}, Payout: ${payoutStatus}`);

  // Process asynchronously
  if (event === 'transactionStatusChanged') {
    if (payoutStatus === 'Paid') {
      // Notify sender, update your database, generate receipt
      await markTransactionComplete(transactionId);
    } else if (complianceStatus === 'Rejected') {
      // Handle rejection — notify sender, initiate refund if needed
      await handleRejection(transactionId);
    }
  }
});

app.listen(3001, () => console.log('Webhook listener on :3001'));

All Endpoints

OperationMethodEndpoint
Register webhookPOST/organizations/{tenant}/webhooks
List webhooksGET/organizations/{tenant}/webhooks
Delete webhookDELETE/organizations/{tenant}/webhooks/{webhookId}

Interactive API Documentation