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

# Create Webhook

> Registers an HTTPS URL to receive outbound webhook integration events.

**How to call** — **POST** `{server}/api/v1/business/webhooks` with `X-API-Key` and JSON body (`url`, `events`, optional `description`). The response includes a **secret** once — store it to verify signatures.

To change URL, events, or pause later without a new secret, use **PATCH** `{server}/api/v1/business/webhooks/{id}`.




## OpenAPI

````yaml /openapi-business.yaml post /api/v1/business/webhooks
openapi: 3.1.0
info:
  title: UGiftMe REST API
  description: >
    REST API for B2B integrations under `/api/v1/business`.


    **Authentication**

    - All documented endpoints require `X-API-Key` (and respect IP whitelisting
    when configured on the key).


    **Async orders**

    When enabled, `POST /orders` may return **202** with `orderRequestId` and
    `status: queued`. Poll `GET /orders/order-requests/{id}`.


    For narrative guides see the **API Guide** documentation page.


    **Error responses**

    Failures return JSON with at least an `error` string. Many endpoints add
    optional fields such as

    `message`, `reason`, `validEvents`, or `details` depending on context. There
    is no global

    `success` / `action` / `code` / `timestamp` envelope on business API routes.

    **`403 Forbidden`** is returned for IP allow-list blocks, incomplete
    business profiles on `/auth`,

    or when the authenticated account cannot access the requested resource.
  version: 1.0.0
  contact:
    name: UGiftMe Developer Support
    email: support@ugift.me
servers:
  - url: https://api-stage.ugift.me
    description: Sandbox
  - url: https://api.ugift.me
    description: Production
security:
  - ApiKeyAuth: []
tags:
  - name: Authentication
    description: API key validation
  - name: Product Catalog
    description: Catalog (API key)
  - name: Orders & Dispatch
    description: Create and list orders (API key)
  - name: Financials
    description: Balances and transactions (API key)
  - name: Webhooks
    description: Outbound webhook registration (API key)
paths:
  /api/v1/business/webhooks:
    post:
      tags:
        - Webhooks
      summary: Create Webhook
      description: >
        Registers an HTTPS URL to receive outbound webhook integration events.


        **How to call** — **POST** `{server}/api/v1/business/webhooks` with
        `X-API-Key` and JSON body (`url`, `events`, optional `description`). The
        response includes a **secret** once — store it to verify signatures.


        To change URL, events, or pause later without a new secret, use
        **PATCH** `{server}/api/v1/business/webhooks/{id}`.
      operationId: registerWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookRegisterRequest'
      responses:
        '201':
          description: Created; `secret` is shown once
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookCreatedResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/AccessDenied'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  schemas:
    WebhookRegisterRequest:
      type: object
      required:
        - url
        - events
      properties:
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
            enum:
              - order.queued
              - order.processing
              - order.succeeded
              - order.failed
              - product.updated
              - wallet.updated
        description:
          type: string
    WebhookCreatedResponse:
      type: object
      properties:
        id:
          type: string
          example: 65d3f4a5b6c7d89012345678
        url:
          type: string
          example: https://hooks.example.com/ugiftme/inbound
        events:
          type: array
          items:
            type: string
          example:
            - order.succeeded
            - wallet.updated
        secret:
          type: string
          description: Shown only at creation time; store securely.
          example: a1b2c3d4e5f678901234567890abcdef1234567890abcdef1234567890abcd
      example:
        id: 65d3f4a5b6c7d89012345678
        url: https://hooks.example.com/ugiftme/inbound
        events:
          - order.succeeded
          - wallet.updated
        secret: a1b2c3d4e5f678901234567890abcdef1234567890abcdef1234567890abcd
    ValidationErrorBody:
      description: >
        Client or validation failure. Always includes `error`; optional
        `message` and endpoint-specific fields.
      oneOf:
        - $ref: '#/components/schemas/ApiErrorWithMessage'
        - $ref: '#/components/schemas/WebhookValidationError'
    UnauthorizedErrorBody:
      description: Missing or invalid `X-API-Key`.
      oneOf:
        - $ref: '#/components/schemas/UnauthorizedMissingApiKey'
        - $ref: '#/components/schemas/UnauthorizedInvalidApiKey'
    AccessDeniedError:
      type: object
      description: >
        Access denied — IP not on the key allow list, incomplete/unverified
        business profile on GET /auth,

        or insufficient permission to the resource (e.g. webhook owned by
        another account).
      required:
        - error
      properties:
        error:
          type: string
        ip:
          type: string
          description: Caller IP when blocked by the key allow list
        whitelistedIps:
          type: array
          items:
            type: string
        status:
          type: string
          description: Business verification or profile status (`GET /auth`)
        missingFields:
          type: array
          items:
            type: string
        message:
          type: string
      additionalProperties: true
    RateLimitError:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: >
            Rate-limit message depends on the route limiter (products, webhooks,
            wallet, etc.).
      example:
        error: Too many product access attempts, please try again later.
    InternalServerError:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Route-specific failure summary
        message:
          type: string
          description: Additional detail (common on webhook and wallet routes)
        details:
          type: string
          description: Additional detail (used on some product catalog routes)
      example:
        error: An error occurred
        message: Optional server detail
    ApiErrorWithMessage:
      type: object
      description: Validation and client errors often add a `message` with more context.
      required:
        - error
      properties:
        error:
          type: string
        message:
          type: string
          description: Additional detail about the failure
      example:
        error: Some required fields are missing
        message: >-
          All fields are required: productCode, merchant, currency,
          valuePurchased
    WebhookValidationError:
      type: object
      required:
        - error
      properties:
        error:
          type: string
        message:
          type: string
        validEvents:
          type: array
          items:
            type: string
      example:
        error: Invalid event types
        message: events must be a non-empty array
        validEvents:
          - order.queued
          - order.processing
          - order.succeeded
          - order.failed
          - product.updated
          - wallet.updated
    UnauthorizedMissingApiKey:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          example: Missing API key
    UnauthorizedInvalidApiKey:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          example: Invalid API key
        reason:
          type: string
          description: Why the key failed validation (when available)
          example: Key has been revoked
  responses:
    ValidationError:
      description: >
        Validation error — required fields missing, invalid format, or business
        rule failure.

        Always includes `error`; many responses also include `message` and
        endpoint-specific fields.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationErrorBody'
          examples:
            orderMissingFields:
              summary: POST /orders — required body fields
              value:
                error: Some required fields are missing
                message: >-
                  All fields are required: productCode, merchant, currency,
                  valuePurchased
            orderIdempotencyConflict:
              summary: POST /orders — idempotency key reuse
              value:
                error: Some required fields are missing
                message: Idempotency-Key reuse with different payload is not allowed
            orderCurrencyMismatch:
              summary: POST /orders — currency mismatch
              value:
                error: Product currency does not match original order currency
                message: Order currency (USD) does not match product currency (GBP)
            webhookInvalidUrl:
              summary: POST /webhooks — invalid URL
              value:
                error: Invalid webhook URL
            webhookInvalidEvents:
              summary: POST /webhooks — invalid events
              value:
                error: Invalid event types
                message: events must be a non-empty array
                validEvents:
                  - order.queued
                  - order.processing
                  - order.succeeded
                  - order.failed
                  - product.updated
                  - wallet.updated
            walletInvalidCurrency:
              summary: GET /wallet/transactions — invalid currency filter
              value:
                error: Some required fields are missing
                message: 'Currency must be one of: GBP, USD, EUR, NGN'
    Unauthorized:
      description: >
        Authentication failed — missing, invalid, or (for some routes)
        unresolvable API key context.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/UnauthorizedErrorBody'
          examples:
            missingApiKey:
              summary: No X-API-Key header
              value:
                error: Missing API key
            invalidApiKey:
              summary: Key failed validation
              value:
                error: Invalid API key
                reason: Key has been revoked
    AccessDenied:
      description: >
        Access denied (HTTP 403). Common causes: caller IP not on the key allow
        list, business profile incomplete

        or unverified on GET /auth, or the authenticated account cannot access
        the resource

        (e.g. another account's webhook).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AccessDeniedError'
          example:
            error: IP not whitelisted
            ip: 203.0.113.10
            whitelistedIps:
              - 198.51.100.0/24
          examples:
            ipNotWhitelisted:
              summary: IP allow list (products, wallet, orders, webhooks)
              value:
                error: IP not whitelisted
                ip: 203.0.113.10
                whitelistedIps:
                  - 198.51.100.0/24
            incompleteBusinessProfile:
              summary: GET /auth — profile incomplete
              value:
                error: Incomplete business profile
                status: incomplete
                missingFields:
                  - businessPhoneNumber
                  - registrationNumber
            businessNotVerified:
              summary: GET /auth — verification pending
              value:
                error: Business verification pending
                status: pending
                message: Complete business verification process
            webhookAccessDenied:
              summary: PATCH or DELETE /webhooks/{id} — wrong account
              value:
                error: You are forbidden from accessing this resource
    RateLimited:
      description: >
        Rate limit exceeded for the route's limiter. Response body is `{
        "error": "<message>" }`.

        Standard rate-limit headers are included when the limiter supports them.
      headers:
        retry-after:
          description: Seconds to wait before retrying
          schema:
            type: integer
            example: 60
        RateLimit-Limit:
          description: Maximum requests allowed in the window
          schema:
            type: integer
            example: 100
        RateLimit-Remaining:
          description: Remaining requests in the current window
          schema:
            type: integer
            example: 0
        RateLimit-Reset:
          description: Unix timestamp when the window resets
          schema:
            type: integer
            example: 1713187200
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/RateLimitError'
          examples:
            productCatalog:
              summary: Product catalog limiter
              value:
                error: Too many product access attempts, please try again later.
            webhooks:
              summary: Webhook configuration limiter
              value:
                error: >-
                  Too many webhook configuration attempts, please try again
                  later.
            wallet:
              summary: Wallet / sensitive-operation limiter
              value:
                error: Too many sensitive operations, please try again later.
    InternalError:
      description: >
        Unexpected server error. Usually `{ "error": "An error occurred" }` from
        the business router,

        or a route-specific failure message with optional `message` / `details`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/InternalServerError'
          examples:
            generic:
              summary: Business router fallback
              value:
                error: An error occurred
                message: An error occurred
            products:
              summary: GET /products failure
              value:
                error: Failed to fetch one or more products
                message: Connection timeout
            productFilters:
              summary: GET /products/filters failure
              value:
                error: Failed to fetch filters
                details: Connection timeout
            webhooks:
              summary: POST /webhooks failure
              value:
                error: Failed to register webhook
                message: Database error
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: |
        Your UGiftMe API key for authentication.

        **Format**: Secure API key (e.g., `<your-api-key>`)

        **Required**: All endpoints require this header

        Example: `X-API-Key: <your-api-key>`

````