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

# Test and debug webhook integrations locally

> Test Yoshi webhook integrations locally with ngrok, send test events, inspect delivery logs, replay failed events, and debug common issues.

Yoshi provides several ways to test your webhook integration before going to production.

## Send a test event

Every endpoint has a test action that sends a synthetic `test.ping` event. Use it to verify your handler is reachable and responding correctly:

```bash theme={null}
curl -X POST https://api.yoshi.ai/v1/webhooks/endpoints/{endpoint_id}/test \
  -H "Authorization: Bearer yoshi_3xK9mP..."
```

```json theme={null}
{
  "data": {
    "sent": true,
    "event_id": "evt_test_a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  },
  "meta": {
    "request_id": "req_a1b2c3d4",
    "timestamp": "2026-04-10T12:00:00.000Z"
  }
}
```

Your endpoint will receive a webhook with this payload:

```json theme={null}
{
  "id": "evt_test_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "type": "test.ping",
  "created_at": "2026-04-10T12:00:00.000Z",
  "api_version": "2026-04-10",
  "data": {
    "test": true,
    "message": "This is a test webhook from Yoshi."
  }
}
```

<Info>
  The `test.ping` event type is synthetic — it's only sent when you explicitly trigger a test. It won't appear in your event subscriptions or filter settings.
</Info>

## Test locally

To receive webhooks on your local machine during development, expose your local server to the internet using a tunnel.

### Using ngrok

<Steps>
  <Step title="Start your local server">
    Run your webhook handler on a local port (e.g., `http://localhost:3000`).
  </Step>

  <Step title="Start ngrok">
    ```bash theme={null}
    ngrok http 3000
    ```

    ngrok outputs a public URL like `https://a1b2c3d4.ngrok-free.app`.
  </Step>

  <Step title="Register the tunnel URL as an endpoint">
    ```bash theme={null}
    curl -X POST https://api.yoshi.ai/v1/webhooks/endpoints \
      -H "Authorization: Bearer yoshi_3xK9mP..." \
      -H "Content-Type: application/json" \
      -d '{
        "url": "https://a1b2c3d4.ngrok-free.app/webhooks/yoshi",
        "description": "Local development"
      }'
    ```

    Save the `secret` from the response — you'll need it for [signature verification](/webhooks/verification).
  </Step>

  <Step title="Send a test event">
    ```bash theme={null}
    curl -X POST https://api.yoshi.ai/v1/webhooks/endpoints/{endpoint_id}/test \
      -H "Authorization: Bearer yoshi_3xK9mP..."
    ```

    You should see the `test.ping` event arrive in your local server logs.
  </Step>
</Steps>

### Using webhook.site

If you just want to inspect webhook payloads without building a handler:

1. Go to [webhook.site](https://webhook.site) to get a temporary URL.
2. Register it as a webhook endpoint via the API.
3. Send a test event or wait for real events to arrive.
4. Inspect the full request headers and body on the webhook.site dashboard.

This is useful for verifying that events are being sent and payloads look correct before writing handler code.

## Inspect delivery logs

View recent delivery attempts via the API:

```bash theme={null}
curl https://api.yoshi.ai/v1/webhooks/deliveries \
  -H "Authorization: Bearer yoshi_3xK9mP..."
```

```json theme={null}
{
  "data": {
    "deliveries": [
      {
        "id": "d1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "event_type": "transaction.created",
        "event_id": "msg_a1b2c3d4",
        "status": "delivered",
        "created_at": "2026-04-10T14:30:00.000Z"
      }
    ]
  },
  "has_more": false,
  "cursor": null,
  "count": 1,
  "meta": {
    "request_id": "req_a1b2c3d4",
    "timestamp": "2026-04-10T15:00:00.000Z"
  }
}
```

The `status` field is one of:

| Status      | Description                                  |
| ----------- | -------------------------------------------- |
| `delivered` | Your endpoint returned `2xx`.                |
| `pending`   | Delivery is in progress or queued for retry. |
| `failed`    | All delivery attempts were exhausted.        |

For more detailed logs, use the [consumer portal](/webhooks/overview#consumer-portal) which shows full request/response details for each delivery attempt.

## Replay failed events

If deliveries fail due to a temporary issue on your end (e.g., a deploy that caused downtime), you can replay them from the consumer portal:

1. Open the portal via `GET /v1/webhooks/portal`.
2. Navigate to the failed deliveries.
3. Click **Replay** to resend individual events or replay all failed events for a time range.

## Debugging checklist

If webhooks aren't arriving at your endpoint:

<AccordionGroup>
  <Accordion title="Is the endpoint URL reachable?">
    Your endpoint must be publicly accessible over HTTPS. Local URLs like `http://localhost:3000` won't work — use a tunnel like ngrok for local development.
  </Accordion>

  <Accordion title="Is the endpoint returning 200?">
    Check `GET /v1/webhooks/deliveries` for the HTTP status code your endpoint returned. Non-`2xx` responses trigger retries.
  </Accordion>

  <Accordion title="Is your handler responding within 15 seconds?">
    If your handler takes too long, the delivery times out and is retried. Return `200` immediately and process the event in the background.
  </Accordion>

  <Accordion title="Is the endpoint active?">
    Check `GET /v1/webhooks/endpoints` to verify the endpoint's `status` is `active`. Endpoints are automatically disabled after repeated failures.
  </Accordion>

  <Accordion title="Are you subscribed to the right event types?">
    If you set `filter_types` when creating the endpoint, only those event types will be delivered. An empty `filter_types` array receives all events.
  </Accordion>

  <Accordion title="Is signature verification failing?">
    If your handler rejects the signature, double-check that you're using the **raw request body** (not a parsed-then-serialized version) and the correct signing secret.
  </Accordion>
</AccordionGroup>
