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

# Create a test-only sandbox account for a person

> Lets an agent create a gigstack account for a person without a partner secret. The account
is a pricing v3 account with **one test API key** (`sk_test_`, `livemode: false`), so it
cannot stamp CFDI before the SAT or charge anything. Going live needs a person: they claim the
sandbox with the code emailed to them, set a password and go live from app.gigstack.pro.

- **Off by default.** The route answers `404 not_found` until the operator enables it.
- **Idempotent.** `Idempotency-Key` is required. Replaying it with the same body returns the
  same `team_id` and `billing_account_id` with `200`, without new effects; the test key is
  returned only in the first `201` response (`api_key.test: null`,
  `credential_delivery: not_replayed` on replay). The same key with a different body is
  `409 idempotency_conflict`.
- **One open sandbox per email.** If the email already has an unclaimed sandbox, a fresh claim
  code is sent to it and the answer is `409 sandbox_pending_claim` with the `team_id` and
  claim details, never keys. Any other existing account is `409 email_already_exists`.
- **Abuse controls.** Per caller IP (3 an hour, 10 a day), per company email domain (50 a
  day; large consumer mailbox providers are exempt), up to 3 claim codes an hour per email,
  and disposable inbox domains are refused with `400 email_not_allowed`.
- **Test data only until claimed.** The test key can use clients, services, payments,
  receipts, invoices, catalogs, SAT lists, retentions and `GET /teams/{id}`. Everything
  else (users, login links, API keys, team members, billing, go-live, portals, webhooks)
  answers `403 sandbox_unclaimed` until the person claims the sandbox.
- An unclaimed sandbox expires 7 days after creation: its keys are revoked and, when the
  person has no other gigstack account, the login is removed so the email can be used
  again. Team data is never deleted.

The claim code is never returned or logged. Hand `team_id` to the person, or ask them for the
code and call `POST /auth/sandbox/claim`.




## OpenAPI

````yaml openapi.json POST /auth/sandbox
openapi: 3.0.3
info:
  title: gigstack API v2
  description: >
    # gigstack API v2


    Build customer, invoice and payment integrations. Start with the
    [quickstart](https://docs.gigstack.io/quickstart), then select the [issuing
    country](https://docs.gigstack.io/concepts/shared-fields).


    ## Authentication and environments


    Use Authorization: Bearer YOUR_API_KEY. Standard live and test keys use
    https://api.gigstack.io/v2. Internal staging uses
    https://gigstack-staging-9z9nnaat.uc.gateway.dev/v2 and separate keys. Test
    mode is a data mode within an environment; a body field does not switch a
    key's mode.


    See [authentication](https://docs.gigstack.io/authentication) for key
    handling, OAuth team scope and gigstack Connect. Internal partner
    provisioning is outside this public contract; health probes have their own
    security declarations. Operations marked unavailable have no configured
    public gateway route.


    ## Read the operation contract


    Response envelopes, pagination and retry rules differ by endpoint. HTTP
    success alone does not prove external fiscal completion or movement of
    money. Reconcile ambiguous writes before retrying. Use [responses and
    recovery](https://docs.gigstack.io/responses) and the operation's error
    codes; document credits and daily limits are not generic retryable rate
    limits.


    The issuing team's configuration selects Mexico or Colombia; customer
    country and currency do not. Read
    [Mexico](https://docs.gigstack.io/countries/mexico) or
    [Colombia](https://docs.gigstack.io/countries/colombia) before choosing
    fiscal fields. Accepted public input differs from a provider's direct
    payload.


    Examples and schema checks do not imply every operation has been executed
    live. [Verification coverage](https://docs.gigstack.io/verification)
    distinguishes source review, offline checks and observed staging behavior.
    Documentation describes operations and does not authorize execution.
  version: 2.0.0
  contact:
    name: Gigstack API Support
    url: https://gigstack.io
    email: support@gigstack.io
  license:
    name: Proprietary
    url: https://gigstack.io/terms
  termsOfService: https://gigstack.io/terms
servers:
  - url: https://api.gigstack.io/v2
    description: >
      Public API for standard live and test-mode keys. Test data is isolated by
      the key mode

      (`livemode: false`). Internal staging is a separate deployment and key
      store; see

      https://docs.gigstack.io/authentication#environments for its base URL.
security:
  - bearerAuth: []
externalDocs:
  description: Complete API Documentation & Guides
  url: https://docs.gigstack.io
paths:
  /auth/sandbox:
    post:
      tags:
        - Auth
      summary: Create a test-only sandbox account for a person
      description: >
        Lets an agent create a gigstack account for a person without a partner
        secret. The account

        is a pricing v3 account with **one test API key** (`sk_test_`,
        `livemode: false`), so it

        cannot stamp CFDI before the SAT or charge anything. Going live needs a
        person: they claim the

        sandbox with the code emailed to them, set a password and go live from
        app.gigstack.pro.


        - **Off by default.** The route answers `404 not_found` until the
        operator enables it.

        - **Idempotent.** `Idempotency-Key` is required. Replaying it with the
        same body returns the
          same `team_id` and `billing_account_id` with `200`, without new effects; the test key is
          returned only in the first `201` response (`api_key.test: null`,
          `credential_delivery: not_replayed` on replay). The same key with a different body is
          `409 idempotency_conflict`.
        - **One open sandbox per email.** If the email already has an unclaimed
        sandbox, a fresh claim
          code is sent to it and the answer is `409 sandbox_pending_claim` with the `team_id` and
          claim details, never keys. Any other existing account is `409 email_already_exists`.
        - **Abuse controls.** Per caller IP (3 an hour, 10 a day), per company
        email domain (50 a
          day; large consumer mailbox providers are exempt), up to 3 claim codes an hour per email,
          and disposable inbox domains are refused with `400 email_not_allowed`.
        - **Test data only until claimed.** The test key can use clients,
        services, payments,
          receipts, invoices, catalogs, SAT lists, retentions and `GET /teams/{id}`. Everything
          else (users, login links, API keys, team members, billing, go-live, portals, webhooks)
          answers `403 sandbox_unclaimed` until the person claims the sandbox.
        - An unclaimed sandbox expires 7 days after creation: its keys are
        revoked and, when the
          person has no other gigstack account, the login is removed so the email can be used
          again. Team data is never deleted.

        The claim code is never returned or logged. Hand `team_id` to the
        person, or ask them for the

        code and call `POST /auth/sandbox/claim`.
      operationId: createAgentSandbox
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          description: >
            Your identifier for this request, 8-128 characters of `A-Z a-z 0-9 .
            _ : -`.
          schema:
            type: string
            minLength: 8
            maxLength: 128
            pattern: ^[A-Za-z0-9._:-]{8,128}$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
                - email
                - name
              properties:
                email:
                  type: string
                  format: email
                  maxLength: 254
                  description: The person's email. The claim code is sent here.
                name:
                  type: string
                  minLength: 1
                  maxLength: 200
                  description: The person's or business name.
                agent:
                  type: object
                  additionalProperties: false
                  properties:
                    client:
                      type: string
                      pattern: ^[A-Za-z0-9 ._:/@+-]{1,64}$
                      description: >-
                        Name of the agent or tool creating the sandbox, kept for
                        audit.
            example:
              email: persona@empresa.mx
              name: Persona Ejemplo
              agent:
                client: claude-code
      responses:
        '200':
          description: >-
            Replay of the same `Idempotency-Key` and body. Same ids, no key, no
            new effects.
          content:
            application/json:
              schema:
                type: object
                required:
                  - success
                  - data
                  - timestamp
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: '#/components/schemas/AgentSandbox'
                  message:
                    type: string
                  timestamp:
                    type: integer
                    format: int64
              example:
                success: true
                data:
                  team_id: team_a1b2c3d4e5f6
                  billing_account_id: ba_a1b2c3d4e5f6g7h
                  livemode: false
                  api_key:
                    test: null
                  credential_delivery: not_replayed
                  claim:
                    sent_to: persona@empresa.mx
                    expires_at: 1767830400000
                message: Sandbox already created; the test key is not returned again
                timestamp: 1767225600000
        '201':
          description: Sandbox created. The test key is returned only in this response.
          content:
            application/json:
              schema:
                type: object
                required:
                  - success
                  - data
                  - timestamp
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: '#/components/schemas/AgentSandbox'
                  message:
                    type: string
                  timestamp:
                    type: integer
                    format: int64
              example:
                success: true
                data:
                  team_id: team_a1b2c3d4e5f6
                  billing_account_id: ba_a1b2c3d4e5f6g7h
                  livemode: false
                  api_key:
                    test: eyJhbGciOiJIUzI1NiJ9.example.signature
                  credential_delivery: first_response
                  claim:
                    sent_to: persona@empresa.mx
                    expires_at: 1767830400000
                    delivery: sent
                message: >-
                  Sandbox created; the test key is returned only in this
                  response
                timestamp: 1767225600000
        '400':
          description: >
            Invalid body or header (`validation_failed`,
            `invalid_request_body`), or a disposable

            inbox domain (`email_not_allowed`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StandardErrorResponse'
              example:
                success: false
                error:
                  code: email_not_allowed
                  message: >-
                    Use a permanent email address the person reads; disposable
                    inboxes are not accepted
                timestamp: 1767225600000
        '404':
          description: Agent sandboxes are not enabled in this environment.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StandardErrorResponse'
              example:
                success: false
                error:
                  code: not_found
                  message: Not found
                timestamp: 1767225600000
        '409':
          description: >
            `sandbox_pending_claim` (a new claim code was sent; `data` has
            `team_id` and `claim`),

            `sandbox_claim_locked` (15 wrong codes over the sandbox's life; it
            is released when it

            expires), `email_already_exists`, `idempotency_conflict`,
            `idempotency_in_progress` or

            `sandbox_outcome_unknown` (do not retry with a new key).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StandardErrorResponse'
              example:
                success: false
                error:
                  code: sandbox_pending_claim
                  message: >-
                    This email has an unclaimed sandbox; a new claim code was
                    sent to it
                data:
                  team_id: team_a1b2c3d4e5f6
                  claim:
                    sent_to: persona@empresa.mx
                    expires_at: 1767830400000
                    delivery: sent
                timestamp: 1767225600000
        '429':
          description: >-
            Per-IP, per-domain or claim-code budget exhausted. `Retry-After`
            says when to retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StandardErrorResponse'
              example:
                success: false
                error:
                  code: rate_limit_exceeded
                  message: Too many sandbox requests. Retry in 1800 seconds.
                timestamp: 1767225600000
        '503':
          description: >-
            Nothing was created, or the state could not be confirmed. Retry the
            same request later.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StandardErrorResponse'
              example:
                success: false
                error:
                  code: sandbox_state_unavailable
                  message: Retry the same request later
                timestamp: 1767225600000
      security: []
components:
  schemas:
    AgentSandbox:
      type: object
      description: A test-only sandbox account created by `POST /auth/sandbox`.
      required:
        - team_id
        - billing_account_id
        - livemode
        - api_key
        - credential_delivery
        - claim
      properties:
        team_id:
          type: string
        billing_account_id:
          type: string
        livemode:
          type: boolean
          enum:
            - false
        api_key:
          type: object
          required:
            - test
          properties:
            test:
              type: string
              nullable: true
              description: >-
                Test-mode key. Returned once, in the first `201`; `null` on
                replay.
        credential_delivery:
          type: string
          enum:
            - first_response
            - not_replayed
        claim:
          type: object
          required:
            - sent_to
            - expires_at
          properties:
            sent_to:
              type: string
              format: email
            expires_at:
              type: integer
              format: int64
              description: Epoch millis. The claim code and the sandbox expire together.
            delivery:
              type: string
              enum:
                - sent
                - failed
              description: >-
                Whether the claim email was handed to the mail provider. On
                `failed`, call again with a new key to re-send.
    StandardErrorResponse:
      type: object
      description: >
        Standardized error envelope emitted by `sendErrorResponse` and its
        helpers

        (`sendValidationError`, `sendNotFoundError`, `sendUnauthorizedError`,

        `sendForbiddenError`, `sendConflictError`, `sendBadRequestError`,

        `sendInternalServerError`).
      required:
        - success
        - error
        - timestamp
      properties:
        success:
          type: boolean
          enum:
            - false
          example: false
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: >
                Stable machine-readable code. Values emitted by the shared
                helpers:

                `unauthorized`, `forbidden`, `invalid_token`,
                `validation_failed`,

                `invalid_request_body`, `missing_required_field`,
                `invalid_field_value`,

                `resource_not_found`, `resource_already_exists`,
                `resource_conflict`,

                `business_rule_violation`, `operation_not_allowed`,

                `insufficient_permissions`, `external_service_error`,

                `payment_processor_error`, `cfdi_service_error`,

                `internal_server_error`, `service_unavailable`,
                `rate_limit_exceeded`,

                `database_error`, `data_integrity_error`, `file_not_found`,

                `file_upload_error`, `invalid_file_format`. Individual handlers
                may

                emit additional endpoint-specific codes, documented per
                operation.
              example: validation_failed
            message:
              type: string
              example: Request validation failed
            details:
              oneOf:
                - type: string
                - type: array
                  items:
                    type: string
              description: >
                Present only when the handler supplies detail. Validation
                failures

                emit an array of `"<field path>: <message>"` strings.
              example:
                - 'currency: Field is required'
        timestamp:
          type: integer
          format: int64
          description: Server time in epoch milliseconds.
          example: 1767225600000
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >
        **Authentication Method:** HTTP Bearer token.


        The runtime requires the literal `Bearer ` prefix — a bare token in the

        `Authorization` header is rejected with `401 unauthorized`.


        **Header Format:** `Authorization: Bearer YOUR_API_KEY`


        Your API key is a JWT. Live keys operate on live data (`livemode:
        true`);

        test keys operate on isolated test data (`livemode: false`).


        **Get your key at:**
        [app.gigstack.pro/settings?tab=api](https://app.gigstack.pro/settings?tab=api)


        **Errors:** credential failures are answered by the authentication layer
        with a raw

        `{ "message": … }` body, not the standardized envelope — `401` for a
        missing, malformed or

        expired token, `403` for a revoked key or a plan without API access. See
        the `Unauthorized`

        and `AuthForbidden` responses.

````

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