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

# Receive a Razorpay webhook delivery

> Verifies the HMAC-SHA256 signature (X-Razorpay-Signature) over the raw request body against RAZORPAY_WEBHOOK_SECRET, timing-safe. A valid, fresh event (deduplicated on X-Razorpay-Event-Id) is persisted to a pending-events store and acknowledged; nothing is processed inline — settlement/lifecycle processing is a future milestone (M4b, see docs/CLAIMS.md). Not registered at all (404) when RAZORPAY_WEBHOOK_SECRET is not configured.




## OpenAPI

````yaml /openapi.bundled.yaml post /webhooks/razorpay
openapi: 3.1.0
info:
  title: Parmana API
  version: 1.0.0
  description: >
    Parmana is an Execution Trust Infrastructure that ensures there is no gap
    between what humans decide and what AI systems do. The API enables creation,
    execution, verification, replay, and auditing of Business Transactions
    through cryptographically verifiable Execution Trust Records.


    **Every route requires a caller bearer key, except GET /health.** Send
    `Authorization: Bearer <key>` on every other request. Keys are issued by
    `scripts/generate-api-key.ts` and configured server-side via
    `PARMANA_API_KEYS`; only a hash of each key is ever held by the server,
    verified in constant time. A missing or invalid credential returns 401
    before a Business Transaction is even constructed, independent of Policy
    evaluation and gateway attestation, see
    `packages/api/src/middleware/caller-auth.ts` and
    [Authentication](/api-reference/authentication). Local development may set
    `PARMANA_AUTH_DISABLED=true` to skip this middleware entirely; that flag
    must never be set in a real deployment.
  contact:
    name: Parmana
    email: support@parmana.ai
  license:
    name: Apache-2.0
    identifier: Apache-2.0
servers:
  - url: http://localhost:3000
    description: Local (packages/api, PORT env var, default 3000)
security:
  - bearerAuth: []
tags:
  - name: Execution
    description: >-
      Executes a Business Transaction through the complete Execution Trust
      pipeline
  - name: Transactions
    description: Business Transaction creation and retrieval
  - name: Verification
    description: Deterministic verification of an Execution Trust Record
  - name: Receipts
    description: Cryptographically signed Execution Trust Receipts
  - name: Trust Records
    description: Execution Trust Record retrieval
  - name: Replay
    description: Deterministic replay of a recorded Execution Trust Record
  - name: Policies
    description: Policy existence/readability check
  - name: Webhooks
    description: Inbound webhook receipt from external connectors (Razorpay)
  - name: System
    description: Operational endpoints
paths:
  /webhooks/razorpay:
    post:
      tags:
        - Webhooks
      summary: Receive a Razorpay webhook delivery
      description: >
        Verifies the HMAC-SHA256 signature (X-Razorpay-Signature) over the raw
        request body against RAZORPAY_WEBHOOK_SECRET, timing-safe. A valid,
        fresh event (deduplicated on X-Razorpay-Event-Id) is persisted to a
        pending-events store and acknowledged; nothing is processed inline —
        settlement/lifecycle processing is a future milestone (M4b, see
        docs/CLAIMS.md). Not registered at all (404) when
        RAZORPAY_WEBHOOK_SECRET is not configured.
      operationId: receiveRazorpayWebhook
      parameters:
        - name: X-Razorpay-Signature
          in: header
          required: true
          schema:
            type: string
          description: hex(HMAC-SHA256(rawBody, RAZORPAY_WEBHOOK_SECRET))
        - name: X-Razorpay-Event-Id
          in: header
          required: true
          schema:
            type: string
          description: >-
            Required once the signature has verified; a request missing it is
            rejected even with a valid signature.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/razorpay-webhook-event.schema'
      responses:
        '200':
          description: Verified event accepted (fresh) or acknowledged (duplicate).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/razorpay-webhook-response.schema'
              examples:
                accepted:
                  summary: Fresh event, persisted
                  value:
                    status: accepted
                acknowledged:
                  summary: Replayed event id, not reprocessed
                  value:
                    status: acknowledged
        '401':
          description: >-
            Missing/invalid signature, or a validly-signed request missing
            X-Razorpay-Event-Id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error.schema'
              examples:
                invalidSignature:
                  summary: Bad signature
                  value:
                    error: Invalid signature.
        '404':
          description: Route not registered — RAZORPAY_WEBHOOK_SECRET is not configured.
        '413':
          description: Request body exceeded the size cap.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error.schema'
              examples:
                tooLarge:
                  summary: Oversized body
                  value:
                    error: Payload too large.
components:
  schemas:
    razorpay-webhook-event.schema:
      title: Razorpay Webhook Event
      description: >-
        Request payload for POST /webhooks/razorpay — Razorpay's own webhook
        envelope, forwarded verbatim. Shape and fields vary by event type; this
        schema only documents the fields this route itself reads (event,
        payload.payment.entity.id, payload.refund.entity.id) for audit metadata
        extraction. The full payload is persisted as-is for later processing
        (M4b) and is not otherwise validated against this schema at runtime —
        signature verification (X-Razorpay-Signature) is what actually
        authenticates a request, not payload shape.
      type: object
      additionalProperties: true
      properties:
        entity:
          type: string
          description: Always "event" for a Razorpay webhook delivery.
          examples:
            - event
        event:
          type: string
          description: >-
            Razorpay event type. Extracted into the pending-events store and
            audit trail when present.
          examples:
            - refund.processed
            - payment.captured
        contains:
          type: array
          items:
            type: string
        payload:
          type: object
          additionalProperties: true
          properties:
            payment:
              type: object
              properties:
                entity:
                  type: object
                  properties:
                    id:
                      type: string
                      examples:
                        - pay_ABC123
            refund:
              type: object
              properties:
                entity:
                  type: object
                  properties:
                    id:
                      type: string
                      examples:
                        - rfnd_ABC123
        created_at:
          type: integer
          description: Unix timestamp.
      examples:
        - entity: event
          event: refund.processed
          contains:
            - refund
          payload:
            refund:
              entity:
                id: rfnd_ABC123
                payment_id: pay_XYZ789
                amount: 100
                status: processed
            payment:
              entity:
                id: pay_XYZ789
          created_at: 1752800000
    razorpay-webhook-response.schema:
      title: Razorpay Webhook Response
      description: >-
        Response body for POST /webhooks/razorpay on a 200. "accepted" means
        this event id was fresh and has been persisted to the pending-events
        store. "acknowledged" means this event id had already been recorded (a
        replay/duplicate delivery) — it was not persisted or reprocessed again.
      type: object
      additionalProperties: false
      required:
        - status
      properties:
        status:
          type: string
          enum:
            - accepted
            - acknowledged
      examples:
        - status: accepted
        - status: acknowledged
    error.schema:
      title: Error Response
      description: >-
        Shared error envelope produced by
        packages/api/src/middleware/error-handler.ts and by every route's inline
        validation checks. error is always a plain human-readable string (never
        a nested object). code is present only when the failure was a
        RuntimeError subclass reaching the centralized error handler
        (VerificationFailedError, ReceiptGenerationError, or an uncategorized
        RuntimeError); it is absent from every inline route-level check
        (businessTransactionId format/required checks) and from
        BusinessTransactionValidationError, PolicyValidationError,
        SignalValidationError, PolicyNotFoundError,
        DuplicateBusinessTransactionError, and the generic 500 fallback. POST
        /policies/validate does NOT use this envelope at all. See its own
        response schema. For the triggering condition and recommended caller
        action behind any specific error/code/status combination, see the Error
        catalog at /api-reference/error-catalog.
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          type: string
          description: Human-readable error message.
        code:
          type: string
          description: >-
            Stable machine-readable error code. Only present for errors that
            reach the handler as a RuntimeError.
          examples:
            - RUNTIME_ERROR
            - VERIFICATION_FAILED
            - RECEIPT_GENERATION_FAILED
      examples:
        - error: businessTransactionId must be a valid UUID.
        - error: >-
            Business Transaction 'eed2a972-1bf5-4166-8472-761f76fbf1b2' already
            exists.
        - error: >-
            Execution rejected: Vendor payment rejected because the assessed
            payment risk exceeds the maximum permitted threshold.
          code: RUNTIME_ERROR
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >
        Caller API key issued by scripts/generate-api-key.ts. Sent as
        Authorization: Bearer <key>. Verified against a stored SHA-256 hash in
        constant time by packages/api/src/auth/StaticKeyAuthenticator.ts.
        Required on every route except GET /health. See
        /api-reference/authentication.

````