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

# Quote a source replay

> Prices a replay of up to 64 consecutive ledgers read again from the RPC history. A quote reserves nothing; start the work with the returned quote_sha256. Use the endpoint's current version (If-Match) and generation. Expired payloads and unsupported ranges are refused. Free Testnet endpoints answer 403 "free Testnet webhooks do not support replay"; paid plan endpoints answer 403 "replays are not yet available for paid webhook endpoints".



## OpenAPI

````yaml /api-reference/webhooks.json post /v1/subscriptions/{id}/source-replays/quote
openapi: 3.0.3
info:
  title: Lumeus Webhooks API
  version: '2026-09-15'
  description: >-
    Lumeus Webhooks API on Stellar. Sign up at https://app.lumeus.dev: Free,
    Starter, Growth and Scale plans are self-serve, Enterprise by agreement.


    - **Pricing.** Testnet webhook deliveries cost no credits. A Free
    organization gets one Testnet endpoint with up to 1,000 deliveries a day. On
    paid plans each delivered Mainnet event costs 1 credit from the monthly
    balance. Calls to this management API are not charged. Plans, limits and
    credit weights: `GET https://api.lumeus.dev/v1/plans`.

    - **Authentication.** Send `Authorization: Bearer whk_…`, a webhook key from
    the dashboard. Webhook keys belong to one network, so pick the server that
    matches the key. `lum_` API keys are not accepted here (401).

    - **Headers.** Every response carries X-Request-Id; authenticated responses
    carry RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; 429 and 503
    carry Retry-After.

    - **Errors.** JSON with a machine-readable code (see
    [errors](/guides/errors)). A path with an empty, `.` or `..` segment answers
    400 invalid_request, a trailing slash answers 404 not_found, and a method
    the path does not accept answers 405 with an Allow header.

    - **CORS.** Browser calls (CORS) are allowed from https://docs.lumeus.dev
    (the Try it playground of this reference) and https://app.lumeus.dev only,
    without cookies; responses expose ETag, Link, Location, Retry-After,
    X-Request-Id, X-Lumeus-Operation-Id, X-Lumeus-Idempotency and
    RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (errors included).
    Other websites cannot read responses made with a server key: use a
    publishable key with your own allowed origins ([browser
    keys](/guides/browser-keys)).
  license:
    name: Lumeus API contract
servers:
  - url: https://webhooks-testnet.lumeus.dev
    description: Testnet (default)
  - url: https://webhooks.lumeus.dev
    description: Mainnet
security:
  - Bearer: []
tags:
  - name: Webhook keys
    description: >-
      Create, list and revoke the `whk_` keys that manage webhook endpoints on
      one network.
  - name: Subscriptions
    description: >-
      Create, list, read, change, pause and delete webhook endpoints
      (subscriptions) and their event filters.
  - name: Deliveries
    description: >-
      What was delivered to an endpoint: recent deliveries, coverage gaps and
      signed test events.
  - name: Signing keys
    description: >-
      Rotate an endpoint's whsec_ signing secret with a 24-hour overlap, or end
      the overlap early.
  - name: Replay
    description: >-
      Redeliver past events or replay a ledger range to an endpoint, priced by a
      quote first. Free and paid plan endpoints get 403 today.
  - name: Health
    description: Liveness and readiness probes for monitoring. No authentication.
paths:
  /v1/subscriptions/{id}/source-replays/quote:
    post:
      tags:
        - Replay
      summary: Quote a source replay
      description: >-
        Prices a replay of up to 64 consecutive ledgers read again from the RPC
        history. A quote reserves nothing; start the work with the returned
        quote_sha256. Use the endpoint's current version (If-Match) and
        generation. Expired payloads and unsupported ranges are refused. Free
        Testnet endpoints answer 403 "free Testnet webhooks do not support
        replay"; paid plan endpoints answer 403 "replays are not yet available
        for paid webhook endpoints".
      operationId: quoteSourceReplay
      parameters:
        - name: id
          in: path
          required: true
          description: >-
            Endpoint (subscription) ID from `POST /v1/subscriptions` or `GET
            /v1/subscriptions`. The example ID is a placeholder and answers 404:
            use your own.
          schema:
            type: string
            format: uuid
            example: 00000000-0000-4000-8000-000000000000
            x-default: 00000000-0000-4000-8000-000000000000
          example: 00000000-0000-4000-8000-000000000000
        - name: If-Match
          in: header
          required: true
          description: >-
            The ETag from `GET /v1/subscriptions/{id}`, for example `"1"` (the
            weak form `W/"1"` that some proxies produce is accepted too). A
            stale version returns 412; a missing header returns 428.
          schema:
            type: string
            pattern: ^(W/)?"[1-9][0-9]*"$
            example: '"1"'
            x-default: '"1"'
          example: '"1"'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourceReplayRequest'
            example:
              generation: testnet-2025-12-18-v2
              first_ledger: 5331500
              last_ledger: 5331520
              max_deliveries: 10
      responses:
        '200':
          description: 'The quote: maximum credits and the hash to start it with.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SourceReplayQuote'
              example:
                protocol: lumeus-source-replay-quote-v1
                subscription_id: 00000000-0000-4000-8000-000000000000
                subscription_version: 2
                network: testnet
                request:
                  generation: testnet-2025-12-18-v2
                  first_ledger: 5331500
                  last_ledger: 5331520
                  max_deliveries: 10
                source_policy_version: prepaid-2026-10
                delivery_policy_version: prepaid-2026-10
                target_sha256: >-
                  bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
                maximum_credits: 12
                source_success_credits: 2
                source_bounds:
                  response_bytes: 1048576
                  timeout_ms: 10000
                  max_events: 128
                  max_payload_bytes: 65536
                source_max_attempts: 3
                resources:
                  bytes: 4096
                  attempts: 8
                  work_ms: 0
                  concurrency: 1
                  retained_bytes: 0
                quote_sha256: >-
                  dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
          headers:
            X-Request-Id:
              description: >-
                Identifier of this request: req_ followed by 24 hex characters.
                Error bodies repeat it as request_id. Quote it when contacting
                support.
              schema:
                type: string
                pattern: ^req_[0-9a-f]{24}$
            RateLimit-Limit:
              description: >-
                Requests per minute of the bucket that answered: the plan rate,
                the dashboard session floor of 120, or a publishable key's
                per-IP rate.
              schema:
                type: integer
            RateLimit-Remaining:
              description: Whole requests left in the bucket after this one.
              schema:
                type: integer
            RateLimit-Reset:
              description: Seconds until the bucket is full again.
              schema:
                type: integer
        '400':
          $ref: '#/components/responses/HTTP400_10'
        '401':
          $ref: '#/components/responses/HTTP401'
        '403':
          $ref: '#/components/responses/HTTP403_3'
        '404':
          $ref: '#/components/responses/HTTP404_2'
        '405':
          $ref: '#/components/responses/HTTP405_13'
        '409':
          $ref: '#/components/responses/HTTP409_2'
        '410':
          $ref: '#/components/responses/HTTP410_2'
        '412':
          $ref: '#/components/responses/HTTP412'
        '413':
          $ref: '#/components/responses/HTTP413'
        '428':
          $ref: '#/components/responses/HTTP428'
        '429':
          $ref: '#/components/responses/HTTP429'
        '503':
          $ref: '#/components/responses/HTTP503_3'
      x-codeSamples:
        - lang: bash
          label: cURL
          source: >-
            # Use a subscription id from POST or GET /v1/subscriptions; the
            placeholder answers 404.

            curl --fail-with-body -sS -X POST
            'https://webhooks-testnet.lumeus.dev/v1/subscriptions/00000000-0000-4000-8000-000000000000/source-replays/quote'
            \
              -H "Authorization: Bearer $LUMEUS_WEBHOOK_KEY" \
              -H 'If-Match: "1"' \
              -H 'Content-Type: application/json' \
              -d '{"generation":"testnet-2025-12-18-v2","first_ledger":5331500,"last_ledger":5331520,"max_deliveries":10}'
        - lang: javascript
          label: JavaScript
          source: >-
            // Node 18+ (ES module)

            // Use a subscription id from POST or GET /v1/subscriptions; the
            placeholder answers 404.

            const res = await
            fetch('https://webhooks-testnet.lumeus.dev/v1/subscriptions/00000000-0000-4000-8000-000000000000/source-replays/quote',
            {
              method: 'POST',
              headers: {
                Authorization: `Bearer ${process.env.LUMEUS_WEBHOOK_KEY}`,
                'If-Match': '"1"',
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                "generation": "testnet-2025-12-18-v2",
                "first_ledger": 5331500,
                "last_ledger": 5331520,
                "max_deliveries": 10
              }),
            });

            console.log(res.status, JSON.stringify(await res.json(), null, 2));
        - lang: python
          label: Python
          source: >-
            # Use a subscription id from POST or GET /v1/subscriptions; the
            placeholder answers 404.

            import json

            import os


            import requests


            res = requests.post(
                "https://webhooks-testnet.lumeus.dev/v1/subscriptions/00000000-0000-4000-8000-000000000000/source-replays/quote",
                headers={"Authorization": f"Bearer {os.environ['LUMEUS_WEBHOOK_KEY']}", "If-Match": "\"1\""},
                json={
                    "generation": "testnet-2025-12-18-v2",
                    "first_ledger": 5331500,
                    "last_ledger": 5331520,
                    "max_deliveries": 10
                },
                timeout=30,
            )

            print(res.status_code, json.dumps(res.json(), indent=2))
components:
  schemas:
    SourceReplayRequest:
      type: object
      properties:
        generation:
          type: string
        first_ledger:
          type: integer
          minimum: 3
        last_ledger:
          type: integer
          description: Inclusive end; must satisfy the selected bounded source policy.
        max_deliveries:
          type: integer
          minimum: 1
          maximum: 128
      required:
        - generation
        - first_ledger
        - last_ledger
        - max_deliveries
      additionalProperties: false
      description: >-
        At most 64 consecutive inclusive ledgers and 128 deliveries, further
        bounded by the selected source policy.
    SourceReplayQuote:
      type: object
      properties:
        protocol:
          type: string
        subscription_id:
          type: string
          format: uuid
        subscription_version:
          type: integer
        network:
          type: string
          enum:
            - mainnet
            - testnet
        request:
          $ref: '#/components/schemas/SourceReplayRequest'
        source_policy_version:
          type: string
        delivery_policy_version:
          type: string
        target_sha256:
          type: string
          pattern: ^[0-9a-fA-F]{64}$
        maximum_credits:
          type: integer
        source_success_credits:
          type: integer
        source_bounds:
          type: object
          properties:
            response_bytes:
              type: integer
            timeout_ms:
              type: integer
            max_events:
              type: integer
            max_payload_bytes:
              type: integer
        source_max_attempts:
          type: integer
        resources:
          $ref: '#/components/schemas/Resources'
        quote_sha256:
          type: string
          pattern: ^[0-9a-fA-F]{64}$
    Resources:
      type: object
      properties:
        bytes:
          type: integer
          description: Conservative resource units; separate from credits.
          minimum: 0
        attempts:
          type: integer
          description: Conservative resource units; separate from credits.
          minimum: 0
        work_ms:
          type: integer
          description: Conservative resource units; separate from credits.
          minimum: 0
        concurrency:
          type: integer
          description: Conservative resource units; separate from credits.
          minimum: 0
        retained_bytes:
          type: integer
          description: Conservative resource units; separate from credits.
          minimum: 0
    Error:
      type: object
      properties:
        error:
          type: string
          description: Human-readable message. It may change; branch on code.
        code:
          type: string
          description: >-
            Machine-readable error code. Branch on code, not on the error text,
            which may change. RPC carries the same codes as the string
            error.data of a JSON-RPC error. invalid_request: 400: invalid input,
            including a malformed Idempotency-Key or an unknown or repeated
            query parameter (Webhooks also uses it for an oversized request
            body, 413). Fix the request; do not retry unchanged. unauthorized:
            401: missing, revoked, invalid or wrong-network credential.
            payment_required: 402 (Mainnet): monthly credit quota exhausted on a
            hard-capped plan. forbidden: 403: the key or account policy does not
            allow this action. not_found: 404 (Webhooks also 410): the resource
            does not exist in this organization and network.
            topic_search_disabled: 404: contract-event topic search is not
            enabled on this network. method_not_allowed: 405: the route does not
            accept this method. conflict: 409: conflicting request identity,
            idempotency key, funding or recovery state. generation_changed: 409:
            the index generation or network changed since the snapshot was
            taken; restart the walk without generation and snapshot_ledger.
            history_unavailable: 409 (Assets): as_of_ledger is not answered from
            the balance history; start a snapshot job (POST /snapshots) or retry
            later. precondition_failed: 412 (Webhooks): the subscription version
            changed; fetch the current ETag. response_too_large: 413: the result
            exceeds the response budget; lower limit or narrow the request.
            precondition_required: 428 (Webhooks): If-Match with the
            subscription ETag is required. rate_limited: 429: a rate,
            concurrency or credit-cap limit was reached, or (Activity, Assets
            and RPC on Testnet) the daily Testnet request cap (message `daily
            Testnet request cap of 10000 reached; resets at 00:00 UTC`,
            Retry-After until 00:00 UTC). Wait Retry-After seconds (a refused
            request consumes nothing). A Retry-After greater than 60 means the
            daily Testnet cap: wait until 00:00 UTC or switch to Mainnet. RPC
            carries it as the string error.data. internal_error: 500 or another
            unexpected 5xx; quote request_id when contacting support.
            not_implemented: 501: the feature is not enabled on this host. busy:
            503: reads are saturated (the queue wait expired or the database
            refused another query or ran short of memory). Retry the same
            request after Retry-After (2) seconds. query_timeout: 503: the read
            ran out of time. Do not retry unchanged right away: narrow the
            ledger range (since_ledger/until_ledger) or lower limit; Retry-After
            is 30. index_not_ready: 503: the index or snapshot is not ready yet
            or is rebuilding; retry after Retry-After seconds. topic_coverage:
            503: the effective range is outside the contract-event topic index
            coverage (the message states it); pass a range inside it or retry
            after Retry-After (60) seconds. unavailable: 503: another temporary
            failure (billing, rate limiter, queue); retry after Retry-After
            seconds.
          enum:
            - invalid_request
            - unauthorized
            - payment_required
            - forbidden
            - not_found
            - topic_search_disabled
            - method_not_allowed
            - conflict
            - generation_changed
            - history_unavailable
            - precondition_failed
            - response_too_large
            - precondition_required
            - rate_limited
            - internal_error
            - not_implemented
            - busy
            - query_timeout
            - index_not_ready
            - topic_coverage
            - unavailable
        request_id:
          type: string
          description: >-
            Identifier of this request, the same value as the X-Request-Id
            header. Quote it when contacting support.
      required:
        - error
  responses:
    HTTP400_10:
      description: >-
        Invalid input (code invalid_request). The message names the problem. Fix
        the request; do not retry it unchanged.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: invalid_request
            error: >-
              replay requires 1–128 distinct terminal deliveries and an exact
              generation/quote
            request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
      headers:
        X-Request-Id:
          description: >-
            Identifier of this request: req_ followed by 24 hex characters.
            Error bodies repeat it as request_id. Quote it when contacting
            support.
          schema:
            type: string
            pattern: ^req_[0-9a-f]{24}$
    HTTP401:
      description: >-
        Missing, invalid, revoked or expired key, or a key for the other network
        (code unauthorized). Keys work only on the network they were created
        for.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            No key:
              summary: No key
              value:
                code: unauthorized
                error: missing credentials
                request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
            Invalid key:
              summary: Unknown, revoked or expired whk_ key
              value:
                code: unauthorized
                error: invalid or revoked API key
                request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
      headers:
        X-Request-Id:
          description: >-
            Identifier of this request: req_ followed by 24 hex characters.
            Error bodies repeat it as request_id. Quote it when contacting
            support.
          schema:
            type: string
            pattern: ^req_[0-9a-f]{24}$
    HTTP403_3:
      description: >-
        This key or account cannot call this route (code forbidden): for example
        a read-only key on a workspace route, or a publishable key on a route or
        from an origin it does not allow.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            Free Testnet endpoint:
              summary: Free Testnet endpoint
              value:
                code: forbidden
                error: free Testnet webhooks do not support replay
                request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
            Paid plan endpoint:
              summary: Paid plan endpoint
              value:
                code: forbidden
                error: replays are not yet available for paid webhook endpoints
                request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
      headers:
        X-Request-Id:
          description: >-
            Identifier of this request: req_ followed by 24 hex characters.
            Error bodies repeat it as request_id. Quote it when contacting
            support.
          schema:
            type: string
            pattern: ^req_[0-9a-f]{24}$
    HTTP404_2:
      description: Not found in this organization and network (code not_found).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: not_found
            error: subscription not found
            request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
      headers:
        X-Request-Id:
          description: >-
            Identifier of this request: req_ followed by 24 hex characters.
            Error bodies repeat it as request_id. Quote it when contacting
            support.
          schema:
            type: string
            pattern: ^req_[0-9a-f]{24}$
    HTTP405_13:
      description: >-
        This path does not accept the HTTP method you sent (code
        method_not_allowed). The Allow header lists the methods it accepts.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: method_not_allowed
            error: >-
              method GET is not allowed on
              /v1/subscriptions/00000000-0000-4000-8000-000000000000/source-replays/quote;
              allowed: POST
            request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
      headers:
        X-Request-Id:
          description: >-
            Identifier of this request: req_ followed by 24 hex characters.
            Error bodies repeat it as request_id. Quote it when contacting
            support.
          schema:
            type: string
            pattern: ^req_[0-9a-f]{24}$
    HTTP409_2:
      description: The request conflicts with the current state (code conflict).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: conflict
            error: request identity or subscription generation conflicts
            request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
      headers:
        X-Request-Id:
          description: >-
            Identifier of this request: req_ followed by 24 hex characters.
            Error bodies repeat it as request_id. Quote it when contacting
            support.
          schema:
            type: string
            pattern: ^req_[0-9a-f]{24}$
    HTTP410_2:
      description: >-
        The resource was deleted, or the retained payload or result expired
        (code not_found).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: not_found
            error: subscription is deleted
            request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
      headers:
        X-Request-Id:
          description: >-
            Identifier of this request: req_ followed by 24 hex characters.
            Error bodies repeat it as request_id. Quote it when contacting
            support.
          schema:
            type: string
            pattern: ^req_[0-9a-f]{24}$
    HTTP412:
      description: >-
        The subscription version changed (code precondition_failed). Fetch the
        current ETag and retry.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: precondition_failed
            error: subscription version changed; fetch its current ETag
            request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
      headers:
        X-Request-Id:
          description: >-
            Identifier of this request: req_ followed by 24 hex characters.
            Error bodies repeat it as request_id. Quote it when contacting
            support.
          schema:
            type: string
            pattern: ^req_[0-9a-f]{24}$
    HTTP413:
      description: Request body too large (code invalid_request).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: invalid_request
            error: request body is too large
            request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
      headers:
        X-Request-Id:
          description: >-
            Identifier of this request: req_ followed by 24 hex characters.
            Error bodies repeat it as request_id. Quote it when contacting
            support.
          schema:
            type: string
            pattern: ^req_[0-9a-f]{24}$
    HTTP428:
      description: If-Match is required (code precondition_required).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: precondition_required
            error: If-Match with the subscription ETag is required
            request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
      headers:
        X-Request-Id:
          description: >-
            Identifier of this request: req_ followed by 24 hex characters.
            Error bodies repeat it as request_id. Quote it when contacting
            support.
          schema:
            type: string
            pattern: ^req_[0-9a-f]{24}$
    HTTP429:
      description: >-
        The Webhooks management rate limit was reached (code rate_limited). Wait
        Retry-After seconds. The daily Testnet request cap of Activity, Assets
        and RPC does not apply to the Webhooks API.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: rate_limited
            error: webhook management rate limit exceeded
            request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
      headers:
        X-Request-Id:
          description: >-
            Identifier of this request: req_ followed by 24 hex characters.
            Error bodies repeat it as request_id. Quote it when contacting
            support.
          schema:
            type: string
            pattern: ^req_[0-9a-f]{24}$
        RateLimit-Limit:
          description: >-
            Requests per minute of the bucket that answered: the plan rate, the
            dashboard session floor of 120, or a publishable key's per-IP rate.
          schema:
            type: integer
        RateLimit-Remaining:
          description: Whole requests left in the bucket after this one.
          schema:
            type: integer
        RateLimit-Reset:
          description: Seconds until the bucket is full again.
          schema:
            type: integer
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
    HTTP503_3:
      description: >-
        Temporarily unavailable (code unavailable), for example the management
        limiter or the database. Wait Retry-After seconds and retry.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: unavailable
            error: webhook control is temporarily unavailable
            request_id: req_6f1c2a9b0e4d4c7a8b3f5e21
      headers:
        X-Request-Id:
          description: >-
            Identifier of this request: req_ followed by 24 hex characters.
            Error bodies repeat it as request_id. Quote it when contacting
            support.
          schema:
            type: string
            pattern: ^req_[0-9a-f]{24}$
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
  securitySchemes:
    Bearer:
      type: http
      scheme: bearer
      description: >-
        Send `Authorization: Bearer whk_…`, a webhook key from the dashboard.
        Webhook keys belong to one network, so pick the server that matches the
        key. `lum_` API keys are not accepted here (401).

````

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