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

# Test your webhook

> Posts a sample payload to one of the webhooks configured on the API key used for this request, and reports whether it was accepted. type=RAMP, the default, posts an order update to rampWebhookURL; type=PAYMENT posts a settled payment to paymentWebhookURL. Either payload has the same shape as the real thing, so a webhook that handles this call handles production traffic. Nothing is created and no order or payment is affected. A webhook that rejects the call still returns 200 — the failure is described in the response body.

Sends a sample payload to a webhook configured on your API key and tells you what
came back. Nothing is created and no real order or payment is touched — this is a
delivery check, not a dry run.

## Choosing which webhook

An API key carries two, and `type` picks between them:

* **`RAMP`** (the default) — posts a sample order to `rampWebhookURL`, the
  endpoint that receives onramp and offramp status updates.
* **`PAYMENT`** — posts a sample settled payment to `paymentWebhookURL`, the
  endpoint that receives [payment](/api-reference/create-payment) settlements.

They are separate because the payloads differ, and a key may have one, both, or
neither. Test each one you rely on; passing on `RAMP` says nothing about whether
your payment handler works.

There is no request body. The URL is read from the key you authenticate with, so
whichever key you send in `x-api-key` is the one whose webhook gets called. That
is deliberate: it verifies the configuration Paj will actually use, not a URL you
retyped into a test form.

## Configuring the URL

Both webhooks live on the API key, alongside its name, and are set from the
dashboard when you create or edit a key. A key without one gets no pushes of that
kind at all; asking to test a webhook the key has not configured is a `400`.

## Reading the result

A rejected delivery is still a successful test, so a webhook that answers `404`
or times out comes back as a `200` with the detail in the body — you do not have
to unpack an error to find out what happened.

* **`delivered`** — `true` only if your endpoint answered with a 2xx. Anything
  else, including a network failure, is `false`.
* **`error`** — present only when `delivered` is `false`. Either the rejecting
  status code or the transport error if the endpoint could not be reached.
* **`durationMs`** — how long the round trip took. Worth watching: a webhook that
  is slow here is slow in production, where a timeout is treated as a failed
  delivery.

## What your endpoint receives

For `RAMP`, a `POST` with an order payload in the same shape as the responses
from [Create an onramp order](/api-reference/create-onramp-order) and [Create an
offramp order](/api-reference/create-offramp-order). The sample describes a
`PROCESSING` Solana offramp.

For `PAYMENT`, a `POST` with the payload from [Create a
payment](/api-reference/create-payment), with `status` at `SUCCESSFUL` — the
same thing a real settlement delivers. The sample describes a 25 USDC payment on
Solana.

Answer with any 2xx. The body is ignored — Paj reads it if there is one, but
nothing depends on its contents.

Either way the delivery is signed exactly as a real one is, with the same secret
and the same `X-PAJ-Timestamp` and `X-PAJ-Signature` headers. A verifier that
accepts this call accepts production traffic, which makes this the cheapest way
to confirm your signature checking works before a real settlement depends on it.
See [Webhook signatures](/concepts/webhook-signatures).

<Warning>
  Both test payloads are fabricated and their `id` does not refer to anything
  real. Make sure a handler that looks records up by id fails softly on an
  unknown one, or your test will report a failure that says more about the test
  than about your webhook.
</Warning>


## OpenAPI

````yaml POST /pub/v2/webhook/test
openapi: 3.0.0
info:
  title: Paj Public API
  description: >-
    Programmatic access to Paj's on/off ramp. Register a Nigerian bank account,
    get

    the on-chain address that pays it, create offramp orders for a specific
    amount,

    and read the live conversion rates.


    Every request must carry an `x-api-key` header. Keys are scoped to a
    business,

    and the rates you receive already have that business's fee applied.
  version: 2.0.0
  contact: {}
servers:
  - url: https://api.paj.cash
    description: Production
security: []
tags: []
paths:
  /pub/v2/webhook/test:
    post:
      tags:
        - Pub V2
      summary: Test your webhook
      description: >-
        Posts a sample payload to one of the webhooks configured on the API key
        used for this request, and reports whether it was accepted. type=RAMP,
        the default, posts an order update to rampWebhookURL; type=PAYMENT posts
        a settled payment to paymentWebhookURL. Either payload has the same
        shape as the real thing, so a webhook that handles this call handles
        production traffic. Nothing is created and no order or payment is
        affected. A webhook that rejects the call still returns 200 — the
        failure is described in the response body.
      operationId: PubV2Controller_testWebhook
      parameters:
        - name: type
          required: false
          in: query
          description: >-
            Which webhook to exercise. `RAMP` posts a sample order to
            `rampWebhookURL`; `PAYMENT` posts a sample settled payment to
            `paymentWebhookURL`.
          schema:
            default: RAMP
            enum:
              - RAMP
              - PAYMENT
            type: string
      responses:
        '200':
          description: The outcome of the delivery attempt
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookTestResultDto'
        '400':
          description: The API key has no URL configured for the requested webhook
        '401':
          description: Missing or invalid API key (x-api-key header)
      security:
        - x-api-key: []
components:
  schemas:
    WebhookTestResultDto:
      type: object
      properties:
        url:
          type: string
          description: The webhook URL configured on the API key that was called
          example: https://example.com/webhook
        delivered:
          type: boolean
          description: True if the endpoint answered with a 2xx
          example: true
        durationMs:
          type: number
          description: Round trip time of the delivery attempt, in milliseconds
          example: 214
        error:
          type: string
          description: >-
            Why the delivery failed — the rejecting status code, or the
            transport error if the endpoint could not be reached at all. Absent
            when delivered.
          example: 'Webhook call failed with status: 404'
      required:
        - url
        - delivered
        - durationMs
  securitySchemes:
    x-api-key:
      type: apiKey
      in: header
      name: x-api-key

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.