> ## Documentation Index
> Fetch the complete documentation index at: https://docs.meterlane.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Circle Webhooks for Corridor Payment Status Updates

> Register a Circle webhook endpoint to receive real-time payment status updates—SETTLED, FAILED, or COMPLIANCE_HOLD—for your corridor payouts automatically.

When you initiate a payout, the payment enters `PENDING` status while Circle processes the fiat leg. Your application cannot poll the `/pay` response for a final result—Circle posts the outcome asynchronously as a webhook event. Meterlane exposes a webhook receiver that Circle calls directly; when a matching event arrives, Meterlane updates the payment status in your account and you can react to it from your own systems.

## Webhook endpoint

Register the following URL as your webhook destination inside the Circle developer dashboard under **Notifications → Webhooks**.

```text theme={null}
POST https://corridor.meterlane.app/webhooks/circle
```

You do not need to build or host a receiver yourself—Meterlane handles ingestion. What you do need is to ensure Circle is configured to send payout events to the address above so the status transitions happen in real time.

## Signature verification

Every request Circle sends to the webhook endpoint includes a `Circle-Signature` header. Meterlane verifies this signature using HMAC SHA-256 computed over the raw request body and your configured webhook secret. Requests that arrive without a valid signature are rejected with a `401` response and never processed.

<Note>
  Meterlane performs the signature check on your behalf. You do not write verification code—just make sure the webhook secret configured in your Meterlane account settings matches the one in your Circle dashboard.
</Note>

The verification algorithm, for reference:

```text theme={null}
HMAC-SHA256(secret, rawBody) == Circle-Signature header value
```

Circle uses a hex-encoded digest. Meterlane compares the values using a constant-time comparison to prevent timing attacks.

## Payment status events

Circle emits three event types that affect corridor payment status. Meterlane maps each event to a final payment status.

| Circle event type            | Payment status    | Meaning                                                                                |
| ---------------------------- | ----------------- | -------------------------------------------------------------------------------------- |
| `payments.payment_completed` | `SETTLED`         | The fiat transfer completed. The beneficiary's bank account has been credited.         |
| `payments.payment_failed`    | `FAILED`          | The transfer could not be completed. Check the dashboard for a failure reason.         |
| `payments.compliance_hold`   | `COMPLIANCE_HOLD` | The payment has been placed on a compliance hold by Circle and requires manual review. |

<Warning>
  A payment in `COMPLIANCE_HOLD` status is not failed—it is paused pending review. Do not re-submit the payout or cancel the quote until you have contacted support and the hold is resolved.
</Warning>

## Example webhook payload

When Circle fires a `payments.payment_completed` event, the payload looks like this:

```json theme={null}
{
  "type": "payments.payment_completed",
  "data": {
    "id": "cpn_7f3a2b1e-4c5d-4e6f-8a9b-0c1d2e3f4a5b"
  }
}
```

The `data.id` field contains the Circle CPN payment ID. Meterlane matches this value against the `cpnPaymentId` returned in the original `/pay` response and updates the corresponding payment record.

<Tip>
  Store the `cpnPaymentId` from the `/pay` response alongside your internal order record. If you need to reconcile a webhook event manually, compare `data.id` in the webhook payload against the `cpnPaymentId` you stored.
</Tip>

## Confirming payout settlement

Because final status arrives via webhook, build your integration around event-driven status updates rather than polling. Here is the recommended pattern:

1. Call `POST /corridors/:id/pay` and store the returned `paymentId` and `cpnPaymentId` in your database.
2. When Meterlane receives the Circle webhook, it updates the payment record to `SETTLED`, `FAILED`, or `COMPLIANCE_HOLD`.
3. Reflect the updated status in your application by checking the payment status via your dashboard or by building a thin status-check endpoint that queries your stored `paymentId`.

<Info>
  Meterlane acknowledges every webhook from Circle with a `{ "received": true }` response immediately after processing, regardless of whether a matching payment record was found. This prevents Circle from retrying deliverable events unnecessarily.
</Info>

## Troubleshooting

| Symptom                                   | Likely cause                  | Resolution                                                                                      |
| ----------------------------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------- |
| Payment stuck in `PENDING`                | Circle webhook not configured | Verify `https://corridor.meterlane.app/webhooks/circle` is registered in your Circle dashboard. |
| Webhooks returning `401`                  | Mismatched webhook secret     | Confirm the secret in Meterlane account settings matches the one Circle is signing with.        |
| `payments.compliance_hold` event received | Compliance review triggered   | Do not resend the payout. Contact Meterlane support with the `paymentId` for guidance.          |
