Skip to main content

Webhook Console

Webhooks can be managed in the Webhook Console. Here, you can
  • Register new webhooks
  • Manage your current webhooks
  • See the webhook documentation

The Webhook Console

Fields

  • Label: The name of your webhook. If your webhook caused an error or did not respond, you will see a danger icon next to the name. If you hover over it, you will see more information
  • Status: Whether your webhook is enabled
  • Topics: Webhooks can register for different topics
  • Ping: After creating a webhook, you can press this button to force sending an event to the webhook in order to trigger it

Register Webhook

Webhooks can be registered in the Partner Area. Click the Webhook navigation entry, and then the “Register Webhook” button.
The webhook URL has to be a https:// URL that points to where you want the webhook to go. You can select which topics you’re interested in for the webhook Once you created the webhook, you will be provided the webhook secret in the next screen. This is needed to validate the webhook signature.

Implement Webhooks

The Partner Area webhook console provides sample code for webhook signature validation. However, additional information can be found below. Overview
  • Webhook ID: newEvent
  • Method: POST
  • Content Type: application/json
  • Authentication: HMAC-SHA256 signature (via headers)
  • Trigger: Emitted when a relevant customer or platform event occurs

Receiving Webhook Requests

Iron will send webhook notifications to the endpoint URL you’ve registered during integration setup. Each request includes headers for verification, and a JSON body describing the event.

HTTP Headers

Iron follows the Standard Webhooks specification: Headers content-type: Always set to application/json. webhook-id: Unique UUID (v4) of the delivery attempt. Use this for logging and idempotency. webhook-timestamp: Unix timestamp (in seconds) indicating when the webhook was sent. webhook-signature : Signature used to verify request authenticity. Format: v1=\<HMAC\_SHA256>

Signature Verification

To ensure webhook authenticity, verify the signature using your secret key:
1
Extract the webhook-timestamp and webhook-signature from the headers.
2
Remove the v1= prefix from the signature.
3
Concatenate webhook-timestamp + raw_body (no whitespace or formatting).
4
Compute the HMAC-SHA256 digest using your webhook secret key.
5
Use constant-time comparison to check if the computed digest matches the signature.
Security Tip:Always verify the signature using constant-time comparison to prevent timing attacks.

Webhook Payload

Webhook requests include a top-level WebhookContainer object. The message field varies depending on the event type. Example Payloads Below, you can find example payloads for all possible events that the webhooks payload can deliver.
// A new transaction happened for this customer
{
  "type": "transaction",
  "timestamp": "2025-06-02T14:59:26.769468+00:00",
  "data": {
    "customer_id": "2ff3e394-978b-4489-8795-0a4e769a04c6",
    "message": {
      "Event": {
        "id": "7d834f68-cea8-496a-8eae-bb0772365028",
        "kind": "Transaction"
      }
    }
  }
}

// A new autoramp was created by / for this customer
{
  "type": "new_autoramp",
  "timestamp": "2025-06-02T14:59:26.769588+00:00",
  "data": {
    "customer_id": "4dcfe5af-3947-4928-b17f-a8fb13a68758",
    "message": {
      "Event": {
        "id": "87f1a893-3691-494d-b815-215f5486a5b4",
        "kind": "NewAutoramp"
      }
    }
  }
}

// A new bank account was registered by / for this customer
{
  "type": "new_bank_account",
  "timestamp": "2025-06-02T14:59:26.769593+00:00",
  "data": {
    "customer_id": "0e98736e-0be6-4f76-9451-ec2fab006dd4",
    "message": {
      "Event": {
        "id": "1cd9daaa-1ad1-48b8-a418-a3964dee1028",
        "kind": "NewBankAccount"
      }
    }
  }
}

// The autoramp with `id` recieved a deposit address
{
  "type": "deposit_address_created",
  "timestamp": "2025-06-02T14:59:26.769598+00:00",
  "data": {
    "customer_id": "45cd0169-7978-49b0-99ed-cb855acdba79",
    "message": {
      "Event": {
        "id": "c8422122-5221-478a-95f4-953157277753",
        "kind": "DepositAddressCreated"
      }
    }
  }
}

// A new customer has been created
{
  "type": "customer_created",
  "timestamp": "2025-06-02T14:59:26.769647+00:00",
  "data": {
    "customer_id": "af0315f0-a683-4330-a7b9-ca0c6a19ce87",
    "message": {
      "Event": {
        "id": "6be8dd21-52a5-4616-b57d-54c4ec8acbf3",
        "kind": "CustomerCreated"
      }
    }
  }
}

// The status of a transaction changed
{
  "type": "transaction_status",
  "timestamp": "2025-06-02T14:59:26.769658+00:00",
  "data": {
    "customer_id": "ebcea2b8-7caf-4ecf-ac59-95c5a6d7fefa",
    "message": {
      "TransactionStatus": {
        "id": "e4f31eb4-da3a-4776-b70e-856a88492a17",
        "status": "Pending"
      }
    }
  }
}

// The status of a fiat address changed
{
  "type": "register_fiat_address_status",
  "timestamp": "2025-06-02T14:59:26.769663+00:00",
  "data": {
    "customer_id": "0ae92a7d-82c5-4e98-ad6e-3dbb6e573b2c",
    "message": {
      "RegisterFiatAddressStatus": {
        "id": "bf1b777d-2f46-4f7c-a5be-b6f825cfcd7b",
        "status": "AuthorizationRequired"
      }
    }
  }
}

// The status of a customer has changed
{
  "type": "customer_status",
  "timestamp": "2025-06-02T14:59:26.769750+00:00",
  "data": {
    "customer_id": "b714a479-0cf8-4390-9f24-d32df02dff36",
    "message": {
      "CustomerStatus": {
        "id": "eb1bbc6a-1256-4653-badf-7d0d7e96deba",
        "status": "Active"
      }
    }
  }
}

// The status of a autoramp has changed
{
  "type": "register_autoramp_status",
  "timestamp": "2025-06-02T14:59:26.769759+00:00",
  "data": {
    "customer_id": "357c34d3-7abe-4334-b8b5-6f8e84ba756e",
    "message": {
      "RegisterAutorampStatus": {
        "id": "bc739df1-2816-4034-8b36-b99510370f18",
        "status": "Created"
      }
    }
  }
}

// A ping event
{
  "type": "ping",
  "timestamp": "2025-06-02T14:59:26.769763+00:00",
  "data": {
    "customer_id": "59f42528-3c5a-4448-b2df-65369adcee42",
    "message": {
      "Ping": {
        "id": "1d9c36fb-83bf-49cc-9b1f-aff970b6ce96"
      }
    }
  }
}

Payload Schema

WebhookContainer (object)
FieldTypeRequiredDescription
typestringYesType of event. See WebhookEventType.
timestampstringYesISO 8601 timestamp of when the event was triggered.
dataobjectYesEvent-specific data payload.
WebhookNotification (inside data)
FieldTypeRequiredDescription
customer_idstringYesUUID of the affected customer.
messageobjectYesThe core event message. Varies by event type.

Supported Event Types

Each message object follows a typed schema depending on the type of event.
  1. WebhookEventMessage
General-purpose events (e.g., onboarding, registration).
{  
  "id": "uuid",  
  "kind": "Transaction" | "NewBankAccount" | "NewAutoramp" | "DepositAddressCreated" | "CustomerCreated"  
}
  1. WebhookFiatAddressStatusMessage
Status of a fiat vIBAN account.
{  
  "id": "uuid",  
  "status": "AuthorizationRequired" | "AuthorizationFailed" | "RegistrationPending" | "RegistrationFailed" | "Registered"  
}
  1. WebhookAutorampStatusMessage
Status changes in an Autoramp.
{  
  "id": "uuid",  
  "status": "Created" | "Authorized" | "Approved" | "Rejected" | "Cancelled"  
}
  1. WebhookTransactionStatusMessage
Real-time updates on transactions.
{  
  "id": "uuid",  
  "status": "Pending" | "PayoutPending" | "Payout" | "PayoutCompleted" | "Completed" | "Failed" | "InAmlReview" | "AmlRejected" | "AmountRejected"  
}
  1. WebhookCustomerStatusMessage
Customer onboarding and compliance status.
{  
  "id": "uuid",  
  "status": "UserRequired" | "SigningsRequired" | "IdentificationRequired" | "Active" | "Suspended"  
}
  1. WebhookPingMessage
Sent during webhook setup or testing.
{  
  "id": "uuid"  
}
  1. WebhookIdentificationStatusMessage
Identification status during KYC/KYB process.
{  
  "id": "uuid",  
  "status": "Pending" | "Processed" | "PendingReview" | "Approved" | "Declined"  
}
Pending → Customer has not started the process Processed → Customer has completed the input process PendingReview → Identification is ready for review by Compliance Team Approved Rejected → Identification has been approved by Compliance Team Rejected → Identification has been rejected by Compliance Team

Response

Your webhook endpoint must return: HTTP/1.1 200 OK Return 200 to acknowledge successful receipt. Any other status code may cause the webhook to be retried.
Note:We recommend logging all webhook webhook-id and response statuses for audit and troubleshooting purposes.

Error Handling & Retries

  • If your service remains unavailable, Iron may pause webhook delivery.
  • Retry attempts include the same webhook-id for deduplication.
  • Webhooks that fail (non-2xx status or timeout) will be retried with exponential backoff.
  • You can see failing webhooks in the Partner Area

Sample Signature

Secret: whsec_1s/keE/2+3eQUBc+7kedMAFRoM0twsrBYPpGWbt2/csF6pbMws9RMDRU1wtRas0PwDYgDd3t7mamKhO4LBjBiQ
Raw payload: {"customer_id":"3f9830ca-a98e-4020-a25b-80f21da86c97","message":{"Ping":{"id":"0196f318-b593-7803-a8f1-047d53179e06"}}}
signatureHeader: v1=85809c7bba57a92bc9766a2af441108ae43f420f27cb1b10ec912c5bc5603a69
timestamp: 1747835371
deliveryId: f22ba628-4ab6-4a01-8d08-ff5de0ca2334

Example Code


<?php

define('WEHO_SECRET', 'whsec_fmKXLgE2fTRYnCCfkRppSGL0JXZPUeqvbdApGrvCQpNd8plfTCMUvgTS8q+/387W3+XphAL8FT44fRIeAmvaTw');
define('TIMESTAMP_TOLERANCE', 5 * 60); // 5 minutes

// Helper: Read raw body and headers
$rawBody = file_get_contents("php://input");
$headers = getallheaders();

$signatureHeader = $headers["webhook-signature"] ?? '';
$timestamp = $headers["webhook-timestamp"] ?? '';
$deliveryId = $headers["webhook-id"] ?? '';

error_log("signatureHeader: $signatureHeader");
error_log("timestamp: $timestamp");
error_log("deliveryId: $deliveryId");

if (!$signatureHeader || !$timestamp || !$deliveryId) {
    http_response_code(400);
    echo "Missing one of required headers: webhook-id, webhook-timestamp, webhook-signature";
    exit;
}

if (!str_starts_with($signatureHeader, "v1=")) {
    http_response_code(400);
    echo "Invalid signature format";
    exit;
}

$receivedSig = substr($signatureHeader, 3);
$ts = intval($timestamp);
if (!$ts) {
    http_response_code(400);
    echo "Invalid webhook-timestamp";
    exit;
}

$now = time();
if (abs($now - $ts) > TIMESTAMP_TOLERANCE) {
    http_response_code(400);
    echo "Webhook timestamp outside of tolerance window";
    exit;
}

$signedPayload = $timestamp . $rawBody;
$computedHmac = hash_hmac('sha256', $signedPayload, WEHO_SECRET);

if (!hash_equals($computedHmac, $receivedSig)) {
    http_response_code(401);
    echo "Invalid signature";
    exit;
}

// Log and respond
$payload = json_decode($rawBody, true);
error_log("Payload: " . print_r($payload, true));
error_log("✔️ Received valid webhook $deliveryId");

header("Content-Type: application/json");
echo json_encode(["status" => "ok"]);