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

# Stream a pull

> Follow a signed pack purchase in real time. The stream reports payment progress and ends with the drawn cards or a failure.

Use your builder key and the buyer’s Permit2 signature over the full pull payload. Results belong to that builder and wallet.

## Request

Builder key and wallet signature

## Response

### HTTP 200 stream events

| When you receive | What to do |
| - | - |
| `pull_started` | Save `data.requestId`. |
| `payment_queued` or `payment_submitting` | Wait for payment confirmation. |
| `payment_confirmed` | Save `data.permitFundTxHash` for recovery. |
| `draws_resolved` | Show all purchased cards. The stream has succeeded. |
| `pull_failed` | Read the failure details. The stream has ended. |

> [accordion] Events and payload fields
>
> Each event carries a JSON `data` object with these common fields.
>
> | Field | Type | Meaning |
> | - | - | - |
> | `buyerWalletAddress` | address | The wallet that signed the pull. |
> | `packId` | uuid | The pack being pulled. |
> | `nonce` | string | The nonce of the signed pull. |
> | `numOfTokens` | integer | How many collectibles the pull buys: 1 to 10. |
> | `message` | string | A short sentence for display. |
>
> **Additional field in `pull_started`**
>
> | Field | Type | Meaning |
> | - | - | - |
> | `requestId` | uuid | Identifies this pull. Poll `GET /v2/gacha/pull/status?requestId=` with it if the stream drops. |
>
> **Additional field in `payment_confirmed`**
>
> | Field | Type | Meaning |
> | - | - | - |
> | `permitFundTxHash` | hash | The funding transaction hash. It is how you recover this pull. |
>
> **Additional fields in `draws_resolved`**
>
> | Field | Type | Meaning |
> | - | - | - |
> | `permitFundTxHash` | hash | The funding transaction hash. |
> | `packName` | string | The pack name. |
> | `pulledAt` | date-time or null | When the funding transaction was mined. |
> | `draws` | array | One entry per collectible, in order. |
> | `draws[].settlementId` | hash | Identifies this collectible for a buyback. |
> | `draws[].tokenId` | string | The collectible's token id. |
> | `draws[].releaseTxHash` | hash or null | Set once the collectible was sent to the wallet. |
> | `draws[].buybackTxHash` | hash or null | Set once the collectible was sold back. |
> | `draws[].tier` | string | The collectible's tier for this draw. |
> | `draws[].buybackBaseValueInUSD` | string | Base value in USD cents, frozen for this draw, bonus included. |
> | `draws[].buybackBonus` | object or null | The buyback bonus of this collectible: `originalBuybackBaseValueInUSD` (the value before the bonus, USD cents) and `bonusBps` (2000 = +20%). The base value above and the `buybackTiers` amounts already include it: do not apply it again. Null when there is no bonus. |
> | `draws[].type` | string | The collectible's category: `POKEMON`, `ONE_PIECE` or `SPORTS`. |
> | `draws[].name` | string | Grade, year, set, number and title. |
> | `draws[].frontImageUrl` | url | The graded slab in its Renaiss frame, on a dark background. |
> | `draws[].attributes` | array | `{ key, value }` pairs: Grader, Grade, Cert Number, Year, Set, Card Number, Character, Language. Empty traits are left out. |
>
> **Additional fields in `pull_failed`**
>
> | Field | Type | Meaning |
> | - | - | - |
> | `code` | string | Why the pull failed. See the failure table. |
> | `error` | string | The same sentence as `message`. |
> | `permitFundTxHash` | hash or null | The funding transaction hash, or null if none was confirmed. |
> | `fundingConfirmed` | boolean | True when the payment was confirmed before the failure. |

### Failures after the stream opens

Handle a `pull_failed` event using `data.code`. Use `data.message` for display. The `data.error` field contains the same text.

After a failure or disconnect, check the original pull before signing again. Payment may already be confirmed.

* If `payment_confirmed` or `pull_failed` supplied `data.permitFundTxHash`, use it to [look up the draw](https://docs.renaiss.xyz/api-reference/gacha/get-draw.md).
* If you only have `data.requestId` from `pull_started`, [check the original request](https://docs.renaiss.xyz/api-reference/gacha/get-pull-status.md).
* If you have neither identifier, [check the wallet’s pull history](https://docs.renaiss.xyz/api-reference/gacha/list-wallet-pulls.md). Use the same recovery steps for an unrecognized code.

When `data.message` is `Unable to fund the pull.`, look up `data.code` below. The table quotes other messages where they apply.

| Error code | Response text and recovery |
| - | - |
| `FUNCTION_ERROR` `GACHA_V3_ON_CHAIN_PACK_ID_NOT_SET` `GACHA_V3_SUB_PACK_LINEUP_LOAD_FAILED` `GACHA_V3_OPEN_PACK_CHECKOUT_BUDGET_ERROR` `GACHA_V3_MERKLE_ROOT_CHECK_FAILED` `GACHA_V3_CHECKOUT_STATUS_UNAVAILABLE` `OPEN_PACK_VRF_FAILED` | `Unable to fund the pull.` Read the original request status before another payment. |
| `GACHA_V3_RESOLVE_DRAWS_NOT_CANONICAL` `GACHA_V3_RESOLVE_DRAWS_NOT_FINALIZED` `GACHA_V3_RESOLVE_DRAWS_PARTIAL_BATCH` `GACHA_V3_RESOLVE_DRAWS_SUBGRAPH_BEHIND` `GACHA_V3_UPDATE_FAILED` `GACHA_V3_PULL_NOT_YET_RESOLVED` `INTERNAL_SERVER_ERROR` | `Payment confirmed, but draw resolution could not finish. Recover using the funding hash.` |
| `CARD_PACK_NOT_ACTIVE` | Show the pack as unavailable and stop retrying. |
| `CARD_PACK_NOT_FOUND` | If `fundingConfirmed` is true, recover the draw using `permitFundTxHash`. If funding failed, check the pack ID before another attempt. |
| `CONTRACT_REVERT` | Look up the original `requestId` before paying again. An uncertain submission can stay `PENDING_PAYMENT`. |
| `CONTRACT_WALLET_CANNOT_RECEIVE_NFT` | Use a wallet that can receive the collectible. |
| `EOA_PURCHASE_REJECTED` | `Permit deadline must be within the next five minutes` `Unable to finish the pull. Reconcile its outcome before retrying.` Check the deadline. If the stream ended unexpectedly, recover the existing pull first. |
| `GACHA_V3_CHECKOUT_ALREADY_PROCESSED` | This pull was already processed. Do not pull again. |
| `GACHA_V3_CHECKOUT_REQUEST_MISMATCH` | The signed pull differs from an earlier pull with the same wallet and nonce. Check the original pull status or wallet pull history before you sign again. |
| `GACHA_V3_DRAW_REVEAL_FAILED` | `Payment confirmed, but collectible details could not be loaded. Recover using the funding hash.` |
| `GACHA_V3_DRAW_UPDATES_UNAVAILABLE` | `Payment confirmed, but live draw updates are unavailable. Recover using the funding hash.` |
| `GACHA_V3_NOT_ENOUGH_CARDS_IN_SET` | Request fewer collectibles or wait. |
| `GACHA_V3_NO_SELLABLE_LINEUP` | Try again when the pack is available. |
| `GACHA_V3_PRICE_MISMATCH` | Read the new price, then prepare and sign again. |
| `GACHA_V3_RESOLVE_DRAWS_FAILED` | `Payment confirmed, but draw resolution could not finish. Recover using the funding hash.` |
| `GACHA_V3_STREAM_TIMEOUT` | `The stream timed out. Check existing funding and draw history before retrying.` The stream ran for 90 seconds without a result. The payment may still be going through. |
| `INSUFFICIENT_ALLOWANCE` | Approve at least `approval.requiredAmount` of USDT for `approval.spender`. Resend the same signed pull before its deadline. |
| `INSUFFICIENT_USDT_AMOUNT` | Add USDT, then resend the same signed pull before its deadline. |
| `OP_WALLET_BUSY` | `Payments are busy right now and nothing was charged. Please try again in a few seconds.` Resend the same signed pull before its deadline. If it has expired, prepare a new one. |
| `PERMIT2_NONCE_ALREADY_USED` | This signed pull was already used. Do not sign it again. |
| `PERMIT_DEADLINE_TOO_FAR` | Prepare again and use the new deadline. |
| `PERMIT_EXPIRED` | The signed pull expired before it was sent, so no payment was made. You can prepare a new pull and sign it again. |
| `WRONG_REQUEST_PARAMS` | Choose an eligible wallet and prepare the pull again. |

On mainnet, a refused USDT transfer can still return `INSUFFICIENT_USDT_AMOUNT` when the allowance is short. Check both the balance and the USDT allowance for Permit2. On testnet, `INSUFFICIENT_ALLOWANCE` identifies a short approval when the balance covers the price.

## Error codes

These HTTP errors can occur before the event stream opens. For guidance on codes shared across endpoints, see [Error meanings and recovery](https://docs.renaiss.xyz/api-reference/errors.md).

| HTTP | Error code | Text field | Response text and recovery |
| :- | :- | :- | :- |
| `400` | `API_AUTH_INVALID_INPUT` | `error` | `Request body is too large` The request body exceeds 16 KiB. Send a smaller body. |
| `400` | `EOA_PURCHASE_REJECTED` | `error` | `SAFE addresses are not supported by v2` The wallet cannot be used for this pull. Use another wallet. |
| `400` | `WRONG_REQUEST_PARAMS` | `message` | `Bad request - invalid parameters` A field is missing or has the wrong form. Fix the request and send it again. |
| `400` | No code | `text/plain` | `Malformed JSON in request body` |
| `401` | `API_AUTH_INVALID_CREDENTIAL` | `error` | `Invalid authentication credential` The builder key is malformed, unknown, expired or revoked. Check the key. Do not retry with the same key. |
| `401` | `API_AUTH_MISSING_CREDENTIAL` | `error` | `Required authentication credential is missing` No builder key was sent. Send your builder key in the `x-builder-api-key` header. |
| `403` | `API_AUTH_CREDENTIAL_NOT_ALLOWED` | `error` | `Credential is not allowed by this policy` This route takes a builder key only. Remove the `Authorization` header and any other credential, then send the request again. |
| `403` | `API_AUTH_INVALID_ACTION` | `error` | `Invalid wallet action proof` The signature was not accepted for the wallet and the prepared data, or the deadline has passed. Prepare again and sign the new data. For a contract wallet, its `isValidSignature` must return `0x1626ba7e` for the EIP-712 digest of the prepared data. |
| `403` | `GACHA_V3_BUILDER_NOT_ACCEPTED_ON_PUBLIC_API` | `error` | `This builder checks out on its own surface, not on the public API` This builder key cannot be used on this API. Do not retry; contact Renaiss. |
| `404` | `EOA_PURCHASE_REJECTED` | `error` | `Pack not found` The pack id is unknown or the pack is not public. Read the pack ids from `GET /v2/gacha/packs`. Do not retry with the same id. |
| `429` | `API_AUTH_RATE_LIMITED` | `error` | `Authentication rate limit exceeded` The builder key hit its rate limit. Wait a little, then retry at a lower rate. |
| `500` | `API_AUTH_ERROR` | `error` | `Internal server error` The signature could not be checked because the chain could not be read. Retry the request. |
| `500` | `FUNCTION_ERROR` | `error` | `Internal server error` |
| `500` | `CARD_PACKS_QUERY_FAILED` | `error` | `Internal server error` Read again after a short wait. |
| `500` | Other code | `error` | `Internal server error` |


## OpenAPI

````yaml api-reference/openapi.json POST /v2/gacha/pull/stream
openapi: 3.1.1
info:
  version: 1.0.0
  title: Renaiss Builder API v2
  description: >-
    Builder API v2 for gacha, marketplace and redemption with external wallets
    on BNB Smart Chain (chain 56). Any active, approved builder key can prepare
    purchases.


    A collectible is an item a wallet can pull, trade, sell back or redeem, such
    as a card. This API calls every such item a collectible (e.g. cards), in
    paths, fields and descriptions.


    Browse publicly, then prepare and sign actions with the buyer wallet. Gacha
    history and buyback offers require your builder key; purchases and buybacks
    also require a wallet action signature. Private shipping data and redemption
    require a wallet access token, which the API issues from a registered
    partner origin after the wallet signs a sign-in message. Gacha and
    marketplace never use a wallet access token. Keep the builder key on your
    backend.


    The acting wallet is an EOA or a deployed ERC-1271 contract account.
    EIP-7702 delegated accounts, ERC-6492 and ERC-8010 wrapped signatures and
    Renaiss-managed Safes are refused (a Safe only after its signature
    verified). A pull to a contract account that cannot receive ERC-721 tokens
    returns `CONTRACT_WALLET_CANNOT_RECEIVE_NFT`. A failed chain read returns
    `API_AUTH_ERROR` (500). Wallet access tokens and redemption are EOA only.
servers:
  - url: https://api.renaiss.xyz
    description: BNB Smart Chain
  - url: https://dev-api.renaiss.xyz
    description: BNB testnet
security: []
tags:
  - name: Gacha v2
    description: >-
      Direct-EOA pack browsing, purchases, collectible (e.g. card) reveals,
      purchase history and buybacks. Start here; no wallet access token is
      needed.
  - name: Marketplace
    description: >-
      Public collectible (e.g. card) browsing and external-wallet asks, bids,
      purchases and cancellations. Trading requires builder authorization and
      wallet action signatures.
  - name: Users
    description: >-
      What one wallet address has and did: its collectibles (e.g. cards), their
      total market value and its activities. The whole wallet, whichever builder
      it used. Requires your builder key only.
  - name: Redemption
    description: >-
      Shipping eligibility, quotes, redemption preparation, submission and
      tracking. Requires your builder key and a wallet access token for the same
      wallet.
  - name: Wallet access tokens
    description: >-
      Get, refresh and revoke the wallet access tokens that Redeem needs. The
      wallet signs a sign-in message (valid 5 minutes, one use); the API returns
      a wallet access token (15 minutes) and a refresh token (7 days, single
      use). Register exact partner frontend origins with Renaiss before use.
      Gacha and marketplace never use a wallet access token.
paths:
  /v2/gacha/pull/stream:
    post:
      tags:
        - Gacha v2
      summary: Submit a signed pull and stream through collectible resolution
      description: >-
        Follow a signed pack purchase in real time. The stream reports payment
        progress and ends with the drawn cards or a failure.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                packId:
                  type: string
                  format: uuid
                numOfTokens:
                  type: integer
                  minimum: 1
                  maximum: 10
                amountPerToken:
                  type: string
                  pattern: ^\d+$
                nonce:
                  type: string
                  pattern: ^\d+$
                deadline:
                  type: integer
                  exclusiveMinimum: 0
                buyerWalletAddress:
                  type: string
                permitSignature:
                  type: string
                  description: >-
                    Signature over the typed data that prepare returned, as hex
                    of at most 4,096 bytes. An EOA signs the typed data. A
                    contract wallet submits what its `isValidSignature` accepts
                    for that data's EIP-712 digest, which can be empty (`0x`);
                    Permit2 is the caller. The signature is checked before
                    payment and again on chain.
              required:
                - packId
                - numOfTokens
                - amountPerToken
                - nonce
                - deadline
                - buyerWalletAddress
                - permitSignature
              additionalProperties: false
      responses:
        '200':
          description: >-
            HTTP `200` opens the event stream. A `draws_resolved` event
            completes the pull successfully. A [`pull_failed`
            event](#failures-after-the-stream-opens) ends the stream with an
            error. Lines starting with `:` are keep-alive comments.
          content:
            text/event-stream:
              schema:
                type: string
              examples:
                success:
                  summary: Successful pull
                  value: >+
                    event: pull_started

                    data:
                    {"buyerWalletAddress":"0x4f2a9c1e7b3d58a60c9e1f2b3d4a5c6e7f8091a2","packId":"3f6c2a1e-8b4d-4c7a-9e21-5d0b7a6c4f13","nonce":"48210937465120938475","numOfTokens":1,"requestId":"6f1c2a9e-4b7d-4e3a-9c15-8a2d0f7b3e64","message":"Your
                    signed pull has been accepted for processing."}


                    event: payment_queued

                    data:
                    {"buyerWalletAddress":"0x4f2a9c1e7b3d58a60c9e1f2b3d4a5c6e7f8091a2","packId":"3f6c2a1e-8b4d-4c7a-9e21-5d0b7a6c4f13","nonce":"48210937465120938475","numOfTokens":1,"message":"Your
                    payment is queued for processing."}


                    event: payment_submitting

                    data:
                    {"buyerWalletAddress":"0x4f2a9c1e7b3d58a60c9e1f2b3d4a5c6e7f8091a2","packId":"3f6c2a1e-8b4d-4c7a-9e21-5d0b7a6c4f13","nonce":"48210937465120938475","numOfTokens":1,"message":"Your
                    payment transaction is being submitted."}


                    event: payment_confirmed

                    data:
                    {"buyerWalletAddress":"0x4f2a9c1e7b3d58a60c9e1f2b3d4a5c6e7f8091a2","packId":"3f6c2a1e-8b4d-4c7a-9e21-5d0b7a6c4f13","nonce":"48210937465120938475","numOfTokens":1,"message":"Payment
                    confirmed. Resolving your
                    collectibles.","permitFundTxHash":"0x9c1d0e4a7b2f6385a1c9d4e7f20b3a6c5d8e1f4a7b0c3d6e9f2a5b8c1d4e7f0a"}


                    event: draws_resolved

                    data:
                    {"buyerWalletAddress":"0x4f2a9c1e7b3d58a60c9e1f2b3d4a5c6e7f8091a2","packId":"3f6c2a1e-8b4d-4c7a-9e21-5d0b7a6c4f13","nonce":"48210937465120938475","numOfTokens":1,"message":"Your
                    collectibles are
                    ready.","permitFundTxHash":"0x9c1d0e4a7b2f6385a1c9d4e7f20b3a6c5d8e1f4a7b0c3d6e9f2a5b8c1d4e7f0a","packName":"Example
                    Pack","pulledAt":"2026-01-15T10:30:00.000Z","draws":[{"settlementId":"0x2b7e4c9a1d5f8036e2a4c7b9d1f3058a6c8e0b2d4f6a8c0e1b3d5f7a9c2e4b60","tokenId":"1234567","releaseTxHash":null,"buybackTxHash":null,"buybackTiers":[{"buybackPercentage":90,"amountInUsdt":"40500000000000000000","expiresAt":"2026-01-15T11:30:04.000Z"},{"buybackPercentage":85,"amountInUsdt":"38250000000000000000","expiresAt":"2026-01-16T10:30:04.000Z"},{"buybackPercentage":80,"amountInUsdt":"36000000000000000000","expiresAt":"2026-01-18T10:30:04.000Z"}],"tier":"rare","buybackBaseValueInUSD":"4500","buybackBonus":{"originalBuybackBaseValueInUSD":"3750","bonusBps":2000},"type":"POKEMON","name":"PSA
                    10 Gem Mint 2025 Example Set #001 Example
                    Collectible","frontImageUrl":"https://example.com/collectibles/example-front.png","attributes":[{"key":"Grader","value":"PSA"},{"key":"Grade","value":"10
                    Gem Mint"},{"key":"Cert
                    Number","value":"PSA00000001"},{"key":"Year","value":"2025"},{"key":"Set","value":"Example
                    Set"},{"key":"Card
                    Number","value":"001"},{"key":"Character","value":"Example
                    Collectible"},{"key":"Language","value":"English"}]}]}

                failure:
                  summary: Failed pull
                  value: >+
                    event: pull_failed

                    data:
                    {"buyerWalletAddress":"0x4f2a9c1e7b3d58a60c9e1f2b3d4a5c6e7f8091a2","packId":"3f6c2a1e-8b4d-4c7a-9e21-5d0b7a6c4f13","nonce":"48210937465120938475","numOfTokens":1,"message":"The
                    stream timed out. Check existing funding and draw history
                    before
                    retrying.","code":"GACHA_V3_STREAM_TIMEOUT","error":"The
                    stream timed out. Check existing funding and draw history
                    before
                    retrying.","permitFundTxHash":"0x9c1d0e4a7b2f6385a1c9d4e7f20b3a6c5d8e1f4a7b0c3d6e9f2a5b8c1d4e7f0a","fundingConfirmed":true}

        '400':
          description: >-
            The request was refused before anything happened.


            See [error codes and
            recovery](/api-reference/gacha/stream-pull#http-errors).
          content:
            text/plain:
              schema:
                type: string
              example: Malformed JSON in request body
            application/json:
              schema:
                anyOf:
                  - type: object
                    properties:
                      error:
                        type: string
                        description: A short sentence about what went wrong.
                      code:
                        type: string
                        enum:
                          - API_AUTH_INVALID_INPUT
                          - EOA_PURCHASE_REJECTED
                    required:
                      - error
                      - code
                  - type: object
                    properties:
                      code:
                        type: string
                        enum:
                          - WRONG_REQUEST_PARAMS
                      message:
                        type: string
                        description: A short sentence about what went wrong.
                    required:
                      - code
                      - message
              example:
                code: WRONG_REQUEST_PARAMS
                message: Bad request - invalid parameters
        '401':
          description: >-
            The builder key is missing or not accepted.


            See [error codes and
            recovery](/api-reference/gacha/stream-pull#http-errors).
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: A short sentence about what went wrong.
                  code:
                    type: string
                    enum:
                      - API_AUTH_MISSING_CREDENTIAL
                      - API_AUTH_INVALID_CREDENTIAL
                required:
                  - error
                  - code
              example:
                error: Required authentication credential is missing
                code: API_AUTH_MISSING_CREDENTIAL
        '403':
          description: >-
            The request is refused.


            See [error codes and
            recovery](/api-reference/gacha/stream-pull#http-errors).
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: A short sentence about what went wrong.
                  code:
                    type: string
                    enum:
                      - API_AUTH_INVALID_ACTION
                      - API_AUTH_CREDENTIAL_NOT_ALLOWED
                      - GACHA_V3_BUILDER_NOT_ACCEPTED_ON_PUBLIC_API
                required:
                  - error
                  - code
              example:
                error: Invalid wallet action proof
                code: API_AUTH_INVALID_ACTION
        '404':
          description: >-
            The pack was not found.


            See [error codes and
            recovery](/api-reference/gacha/stream-pull#http-errors).
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: A short sentence about what went wrong.
                  code:
                    type: string
                    enum:
                      - EOA_PURCHASE_REJECTED
                required:
                  - error
                  - code
              example:
                error: Pack not found
                code: EOA_PURCHASE_REJECTED
        '429':
          description: >-
            Too many requests with this builder key.


            See [error codes and
            recovery](/api-reference/gacha/stream-pull#http-errors).
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: A short sentence about what went wrong.
                  code:
                    type: string
                    enum:
                      - API_AUTH_RATE_LIMITED
                required:
                  - error
                  - code
              example:
                error: Authentication rate limit exceeded
                code: API_AUTH_RATE_LIMITED
        '500':
          description: >-
            A failure on our side before the stream started. Nothing was
            charged. Retry later.


            See [error codes and
            recovery](/api-reference/gacha/stream-pull#http-errors).
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: A short sentence about what went wrong.
                  code:
                    type: string
                required:
                  - error
                  - code
              example:
                error: Internal server error
                code: FUNCTION_ERROR
      security:
        - builderAuth: []
components:
  securitySchemes:
    builderAuth:
      type: apiKey
      in: header
      name: x-builder-api-key
      description: >-
        Your approved builder API key. Keep it on your backend. Gacha and
        marketplace writes also require wallet action signatures. Redeem also
        needs a wallet access token, a Bearer token the API issues after the
        wallet signs a sign-in message.

````

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