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

# Register a dispute webhook

> Registers a dispute webhook for the merchant identified by the secret key and the
`X-Merchant-Code` header. Disputes have no test mode: authorize with a production
secret key (`sk_...`) — a request signed with a test key (`sk_test_...`) is rejected
with `403`. Each `merchant_code` can have up to 4 dispute webhooks; the limit is
separate from payment webhooks. The `url` is normalised before it is stored
(lower-case scheme and host, default port and trailing slash removed) and must be
unique per merchant. `header.value` is a secret: it is accepted in full and returned
masked in every response, including this one — `****` followed by the last 4 characters
for values longer than 8 characters, `****` alone for shorter values.


<Note>
  This is a public, self-service endpoint. Call `https://api.tabby.ai/api/v1/dispute-webhooks` (`https://api.tabby.sa/api/v1/dispute-webhooks` for KSA) with your **live** secret key and the `X-Merchant-Code` header. How dispute webhooks work, payload and statuses: <a href="/pay-in-4-custom-integration/dispute-webhooks">Dispute webhooks</a>.
</Note>


## OpenAPI

````yaml post /api/v1/dispute-webhooks
openapi: 3.1.0
info:
  title: Tabby API Reference
  version: 1.0.0
  x-logo:
    url: assets/tabby-new.png
    altText: tabby Logo
  description: |
    Tabby Documentation:  **[docs.tabby.ai](https://docs.tabby.ai/)**
servers:
  - url: https://api.tabby.ai/
    description: Production (UAE)
  - url: https://api.tabby.sa/
    description: Production (KSA)
security: []
tags:
  - name: Checkout
    description: >-
      Checkout is a whole process of customer data collection and payment
      authorization.
  - name: Payments
    description: >-
      The core of tabby is a payments flow enabling you to handle payments at
      your webstore.
  - name: Webhooks
    description: Manage webhook endpoints.
  - name: Dispute webhooks
    description: >-
      Manage dispute webhook endpoints. Live secret key and `X-Merchant-Code` on
      every request; disputes have no test mode.
  - name: Disputes
    description: >-
      The Disputes API is designed to simplify the process of handling disputes
      and help merchants resolve issues with customer orders more efficiently.
      Merchants can use this API to retrieve a list of disputes, view details
      about a specific dispute, and approve or challenge a bunch of disputes. It
      mirrors the functionality available in Tabby Merchant Dashboard and
      operates exclusively with live payments using live credentials. A secret
      key is required for using the Disputes API. <SchemaDefinition
      schemaRef="#/components/schemas/Webhook" />
paths:
  /api/v1/dispute-webhooks:
    post:
      tags:
        - Dispute webhooks
      summary: Register a dispute webhook
      description: >
        Registers a dispute webhook for the merchant identified by the secret
        key and the

        `X-Merchant-Code` header. Disputes have no test mode: authorize with a
        production

        secret key (`sk_...`) — a request signed with a test key (`sk_test_...`)
        is rejected

        with `403`. Each `merchant_code` can have up to 4 dispute webhooks; the
        limit is

        separate from payment webhooks. The `url` is normalised before it is
        stored

        (lower-case scheme and host, default port and trailing slash removed)
        and must be

        unique per merchant. `header.value` is a secret: it is accepted in full
        and returned

        masked in every response, including this one — `****` followed by the
        last 4 characters

        for values longer than 8 characters, `****` alone for shorter values.
      operationId: postDisputeWebhook
      parameters:
        - $ref: '#/components/parameters/disputeMerchantCodeParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DisputeWebhookRequest'
      responses:
        '200':
          $ref: '#/components/responses/DisputeWebhook'
        '400':
          $ref: '#/components/responses/BadRequestError_bad_request'
        '401':
          $ref: '#/components/responses/AuthenticationError_invalid_secret_key'
        '403':
          $ref: '#/components/responses/ForbiddenError_no_test_mode'
        '500':
          $ref: '#/components/responses/UnexpectedError_internal'
      security:
        - bearerAuth:
            - secret_key
components:
  parameters:
    disputeMerchantCodeParam:
      in: header
      name: X-Merchant-Code
      required: true
      schema:
        type: string
        example: code provided to you from Tabby side
      description: >-
        Merchant code the request is scoped to. Required on every
        dispute-webhook request, even when your secret key maps to a single
        merchant; case-insensitive.
  schemas:
    DisputeWebhookRequest:
      type: object
      properties:
        url:
          type: string
          format: uri
          example: https://example.com/tabby/dispute-webhook
          description: >-
            Endpoint for dispute notifications (HTTPS recommended). Must be an
            absolute URL with a publicly resolvable host name — `localhost`, raw
            IP addresses and hosts that do not resolve are rejected. Normalised
            before it is stored (lower-case scheme and host, default port and
            trailing slash removed) and unique per merchant.
        header:
          type: object
          description: >-
            Optional static header Tabby adds to every notification so you can
            verify its origin. Omit it on `PUT` to remove the header.
          properties:
            title:
              type: string
              example: X-Auth-Key
              description: Header name (an HTTP token, up to 128 characters).
            value:
              type: string
              example: your_random_signing_secret
              description: >-
                Header value (up to 1024 characters). Treated as a secret —
                every response returns it masked.
          required:
            - title
            - value
      required:
        - url
    DisputeWebhook:
      type: object
      properties:
        id:
          type: string
          readOnly: true
          example: f5eb7c26-b163-4fad-b75f-66024824116e
          description: Unique dispute webhook ID, assigned by Tabby.
        url:
          type: string
          format: uri
          example: https://example.com/tabby/dispute-webhook
          description: Endpoint for dispute notifications, as stored (normalised).
        header:
          type: object
          nullable: true
          description: The signing header, if one was registered. `value` is masked.
          properties:
            title:
              type: string
              example: X-Auth-Key
            value:
              type: string
              example: '****cret'
              description: >-
                Masked — `****` followed by the last 4 characters of the stored
                value; `****` alone when the value has 8 characters or fewer.
      required:
        - id
        - url
    Error_400_bad_request:
      type: object
      properties:
        status:
          type: string
          example: error
        errorType:
          type: string
          example: bad_data
        error:
          type: string
          example: bad_request
    Error_401_invalid_secret_key:
      type: object
      properties:
        status:
          type: string
          example: error
        errorType:
          type: string
          example: not_authorized
        error:
          type: string
          example: invalid secret key
    Error_403_no_test_mode:
      type: object
      properties:
        status:
          type: string
          example: error
        errorType:
          type: string
          example: no_permission
        error:
          type: string
          example: disputes have no test mode
    Error_500_internal:
      type: object
      properties:
        status:
          type: string
          example: error
        errorType:
          type: string
          example: internal
        error:
          type: string
          example: internal
  responses:
    DisputeWebhook:
      description: Success. Dispute webhook object is returned; `header.value` is masked.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DisputeWebhook'
    BadRequestError_bad_request:
      description: >-
        One of the required fields is missing or request is not formatted
        correctly.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error_400_bad_request'
    AuthenticationError_invalid_secret_key:
      description: The request cannot be authorized.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error_401_invalid_secret_key'
    ForbiddenError_no_test_mode:
      description: >-
        Disputes have no test mode — the request was authorized with a test
        secret key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error_403_no_test_mode'
    UnexpectedError_internal:
      description: Something bad happened. We're notified.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error_500_internal'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >
        Bearer authentication header of the form `Bearer <secret_key>`, where
        `<secret_key>` is your `secret_key`.

````