Webhooks

Webhooks deliver real-time notifications when events occur in an Martis account. Instead of polling the API for updates, webhooks push event data to a configured endpoint, enabling immediate response to payment completions, payout status changes, and other critical events.


How Webhooks Work

When an event occurs (e.g., a payment succeeds), Martis sends an HTTP POST request to the configured webhook endpoint with event details in the request body. The receiving server processes the event and returns a 2xx status code to acknowledge receipt.

StepDescription
1. Event occursA payment charge succeeds, a payout completes, etc.
2. Webhook sentMartis sends a POST request to the registered endpoint
3. Signature verificationThe receiving server verifies the request signature
4. Event processingThe server processes the event data
5. AcknowledgmentThe server returns a 2xx response

Configure Webhook Endpoints

Create an Endpoint

Webhook endpoints must meet the following requirements:

  • HTTPS — Only secure endpoints are supported
  • POST method — Endpoints must accept POST requests
  • JSON content — Request bodies are JSON-formatted
  • 2xx response — Return a success status code to acknowledge receipt

Register in Dashboard

  1. Navigate to Creator Hub → Integration → Webhooks

    Webhooks Dashboard

  2. Click Add Webhook Destination

  3. Enter the HTTPS endpoint URL and optional description

  4. Save the configuration

Manage Endpoints

Registered endpoints can be viewed, updated, or deleted from the dashboard. Each endpoint displays delivery statistics and recent event history.


Signature Verification

Verify webhook signatures to ensure requests originate from Martis and have not been tampered with.

Signature Components

Each webhook request includes two headers for verification:

HeaderDescription
{HEADER_TIMESTAMP}Unix timestamp when the webhook was sent
{HEADER_SIGNATURE}HMAC-SHA256 signature of the request

Verification Process

  1. Extract the timestamp and signature from request headers
  2. Construct the signed payload: {timestamp}.{raw_body}
  3. Compute HMAC-SHA256 using the webhook secret
  4. Compare the computed signature with the received signature

Signature Components

Implementation Examples

Signature Verification

using System.Security.Cryptography;
using System.Text;

public static class WebhookVerification
{
    public static bool VerifySignature(
        string rawBody,
        string timestamp,
        string receivedSignature,
        string webhookSecret)
    {
        var payload = $"{timestamp}.{rawBody}";
        var expectedSignature = ComputeHmacSha256(payload, webhookSecret);
        
        return CryptographicOperations.FixedTimeEquals(
            Encoding.UTF8.GetBytes(expectedSignature),
            Encoding.UTF8.GetBytes(receivedSignature)
        );
    }

    private static string ComputeHmacSha256(string data, string key)
    {
        using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
        var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
        return Convert.ToHexString(hash).ToLower();
    }
}

Event Types

Payment Charge Events

Events triggered by payment charge status changes:

EventDescription
payment_charge.createdA new payment charge was created
payment_charge.updatePayment charge details were updated
payment_charge.succeededPayment was completed successfully
payment_charge.failedPayment failed or expired

Payment Charge Payload Example

payment_charge.succeeded

{
  "id": "evt_01HZEVT123456ABCDEF",
  "type": "payment_charge.succeeded",
  "created_at": "2025-01-15T11:15:00Z",
  "data": {
    "id": "01HZCHARGE123456ABCDEF",
    "payment_method": "qris",
    "channel": "gudang_voucher",
    "status": "success",
    "currency": "idr",
    "amount": 50000.00,
    "fee": 350.00,
    "client_reference_id": "ORDER-001",
    "customer_details": {
      "name": "John Doe",
      "email": "john@example.com",
      "phone_number": "081234567890"
    },
    "items": [
      {
        "name": "Product A",
        "price": 50000.00,
        "quantity": 1
      }
    ],
    "description": "Payment for Order #001",
    "expires_at": "2025-01-15T11:30:00Z",
    "paid_at": "2025-01-15T11:15:00Z",
    "created_at": "2025-01-15T11:00:00Z",
    "updated_at": "2025-01-15T11:15:00Z"
  }
}

Payout Events

Events triggered by payout status changes:

EventDescription
payout.createdA new payout was created
payout.updatedPayout details were updated
payout.pendingPayout is awaiting processing
payout.processingPayout is being processed
payout.succeededPayout was completed successfully
payout.failedPayout failed
payout.suspectedPayout was flagged for review
payout.rejectedPayout was rejected after review
payout.refundedPayout was refunded to source account

Payout Payload Example

payout.succeeded

{
  "id": "evt_01HZEVT789012GHIJKL",
  "type": "payout.succeeded",
  "created_at": "2025-01-15T14:30:00Z",
  "data": {
    "id": "01HZPAYOUT123456ABCDEF",
    "account_id": "01HZABC987654321FEDCBA",
    "currency": "idr",
    "status": "completed",
    "type": "bank_account",
    "bank": {
      "code": "bca",
      "name": "Bank Central Asia",
      "country_code": "ID"
    },
    "account_number": "1234567890",
    "account_holder_name": "John Doe",
    "amount": 500000.00,
    "fee": 5000.00,
    "description": "Vendor payment",
    "client_reference_id": "PO-2024-0001",
    "created_at": "2025-01-15T14:00:00Z",
    "updated_at": "2025-01-15T14:30:00Z"
  }
}

Retry Mechanism

When a webhook delivery fails, Martis automatically retries the request using exponential backoff.

Retry Policy

AttemptDelayCumulative Time
1Immediate0
25 minutes5 minutes
330 minutes35 minutes

Failure Conditions

A delivery is considered failed when:

  • The endpoint returns a non-2xx status code
  • The request times out (30 seconds)
  • The endpoint is unreachable

Best Practices

Endpoint Implementation

  • Return quickly — Acknowledge receipt immediately, then process asynchronously
  • Handle duplicates — Use event IDs to implement idempotent processing
  • Verify signatures — Always validate the signature before processing
  • Log events — Maintain logs for debugging and auditing

Error Handling

  • Return appropriate status codes — Use 2xx for success, 4xx for client errors, 5xx for server errors
  • Implement retry logic — Design handlers to safely process retried events
  • Monitor failures — Set up alerts for failed webhook deliveries

Security

  • Use HTTPS — Never expose HTTP endpoints for webhooks
  • Validate signatures — Reject requests with invalid or missing signatures
  • Verify timestamps — Reject requests with stale timestamps (e.g., older than 5 minutes)
  • Restrict access — Only allow traffic from Martis IP ranges if possible

Testing Webhooks

Sandbox Testing

Use the sandbox environment to test webhook integration:

  1. Configure a webhook endpoint in the sandbox dashboard
  2. Create test payment charges or payouts
  3. Verify webhook delivery and signature validation
  4. Test error handling and retry scenarios

Local Development

For local development, use a tunneling service to expose a local endpoint:

# Example using ngrok
ngrok http 3000

# Use the generated HTTPS URL as the webhook endpoint
# https://abc123.ngrok.io/webhook

Troubleshooting

IssueSolution
Webhooks not receivedVerify endpoint URL is correct and accessible
Signature verification failsEnsure raw body is used without modification
Duplicate events receivedImplement idempotent processing using event IDs
TimeoutsReduce processing time; acknowledge quickly and process async
SSL errorsVerify SSL certificate is valid and properly configured

Was this page helpful?