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

> ## Agent Instructions
> Notes on the Listo Partner Delivery API:
> - Every endpoint is under `https://api.listo.delivery/v1/` and authenticates with a bearer API key.
> - This documentation site is at https://docs.listo.delivery. The full machine-readable reference is the OpenAPI document at https://api.listo.delivery/docs/openapi.yaml (served by the API host, not by this site).
> - Any page of this site is also available as raw markdown by appending `.md` to its URL, for example https://docs.listo.delivery/en/guides/webhooks.md. The index of every page is https://docs.listo.delivery/llms.txt and all of them in one file is https://docs.listo.delivery/llms-full.txt.
> - Every page is published in English under https://docs.listo.delivery/en/ and in Spanish under https://docs.listo.delivery/es/, with the same path after the language. The API reference is under https://docs.listo.delivery/api-reference/ in English and https://docs.listo.delivery/es/api-reference/ in Spanish.
> - To debug an integration yourself: `GET /v1/request_logs` lists the calls your keys made with what you sent and what was answered, `GET /v1/events` lists the events Listo emitted, `GET /v1/webhook_deliveries` lists every attempt to deliver them, `POST /v1/events/{id}/resend` sends one event again and `POST /v1/test_webhooks` sends a sample event (`"test": true`). The page https://docs.listo.delivery/en/guides/debugging describes them. A resent event is a snapshot from when it happened; `GET /v1/deliveries` is still how you recover current state.
> - Every error response has `error.doc_url`, a page that explains that error code and what to do, and `error.request_id`, the same value as the `X-Request-Id` response header. All the pages are listed at https://docs.listo.delivery/en/errors. Filter `GET /v1/request_logs` with `request_id` to read the logged call.
> - A `409 account_exists` from `POST /v1/stores` means the restaurant already has its own Listo account: connect it instead of creating it, as the page https://docs.listo.delivery/en/guides/connect-existing-account describes.
> - Every live store carries one Plan Listo, charged to the partner. The exception is a connected restaurant that had already paid its current month: it keeps paying its own plan until its next renewal, shown as `paid_by: "store"` with the handover date in `pending_paid_by_on`. A `402 plan_required` from a quote, a delivery create or a retry means the store's plan is not paid or has ended; the page https://docs.listo.delivery/en/guides/plan-and-billing describes the plan, the `plan` block on a store and how to end or restart it.
> - An AI assistant (Claude Code, Cursor, Codex) can act through the same API key at `https://api.listo.delivery/mcp`, an MCP server over Streamable HTTP authenticated with `Authorization: Bearer <API key>`. Every tool runs the matching `/v1/` endpoint, and each one that moves money first previews and then needs a confirmation token; the page https://docs.listo.delivery/en/guides/connect-ai-client lists the tools and describes the two steps.

# Reactivate a store's plan

> Restarts a plan that ended. The first month is charged in this request,
from your live balance or your saved card, and the response is the
store with `plan.status` `active`.

`Idempotency-Key` is required, and a repeat with the same key answers
the original response. Live keys only: a test key gets `404 not_found`.

`402 insufficient_balance` when neither your balance nor your saved card
can pay the month: nothing was charged and the plan stays ended, and the
`message` gives what is available and what is due. `409
not_reactivatable` when the plan has not ended: a `canceling` plan is
undone with `DELETE /v1/stores/{store_id}/plan_cancellation`, and a
`past_due` one starts again when its charge goes through, and a store
whose first month is still `awaiting_payment` is charged by itself as
soon as your balance or card covers it. It also
answers when the restaurant pays its own plan, which it buys in its own
console and is not yours to reactivate. `409
charge_in_progress`, which is `retryable`, while a card charge for the
plan is still with the bank: nothing new was charged, so read the store
again shortly.




## OpenAPI

````yaml /openapi/en.yaml post /v1/stores/{store_id}/plan
openapi: 3.1.0
info:
  title: Listo Delivery API
  version: 1.34.0
  description: |
    Quote a delivery, dispatch it, and follow it to the door.

    Every request authenticates with a bearer API key. The key itself decides
    the world it acts in: a test key dispatches simulated couriers and moves no
    money, a live key dispatches real ones. A key can also be read-only: it
    may call GET and HEAD and nothing else, and any other call answers
    `403 read_only_key`. Money is always in Mexican pesos and
    always in integer cents, and every timestamp is ISO-8601 in UTC.

    Ids are opaque prefixed strings (`st_` a store, `qt_` a quote, `dlv_` a
    delivery, `wd_` a withdrawal, `evt_` an event, `le_` a ledger entry,
    `whd_` a webhook delivery attempt, `req_` a logged request). Treat
    them as strings of unbounded length and never parse them.

    Changes to v1 are additive: new fields and new enum values can appear, so
    ignore what you do not recognise rather than failing on it.
servers:
  - url: https://api.listo.delivery
security: []
tags:
  - name: Authentication
    description: Checking that a key works before you build against it.
  - name: Stores
    description: The pickup locations you dispatch from.
  - name: Account connection
    description: Connecting a restaurant's existing Listo account to you.
  - name: Quotes
    description: What a delivery costs, priced and held.
  - name: Deliveries
    description: Dispatching an order and following it to the door.
  - name: Balance
    description: The prepaid balance your deliveries draw on.
  - name: Withdrawals
    description: Moving money out of that balance to your bank.
  - name: Card onboarding
    description: Connecting the account that takes card payments.
  - name: Events
    description: >-
      The events Listo sent you, resending one, and sending a test event to your
      endpoint.
  - name: Webhook deliveries
    description: >-
      Every attempt Listo made to send an event to your endpoint, and what came
      back.
  - name: Request logs
    description: >-
      Every call your keys made to this API, with what you sent and what we
      answered.
paths:
  /v1/stores/{store_id}/plan:
    post:
      tags:
        - Stores
      summary: Reactivate a store's plan
      description: |
        Restarts a plan that ended. The first month is charged in this request,
        from your live balance or your saved card, and the response is the
        store with `plan.status` `active`.

        `Idempotency-Key` is required, and a repeat with the same key answers
        the original response. Live keys only: a test key gets `404 not_found`.

        `402 insufficient_balance` when neither your balance nor your saved card
        can pay the month: nothing was charged and the plan stays ended, and the
        `message` gives what is available and what is due. `409
        not_reactivatable` when the plan has not ended: a `canceling` plan is
        undone with `DELETE /v1/stores/{store_id}/plan_cancellation`, and a
        `past_due` one starts again when its charge goes through, and a store
        whose first month is still `awaiting_payment` is charged by itself as
        soon as your balance or card covers it. It also
        answers when the restaurant pays its own plan, which it buys in its own
        console and is not yours to reactivate. `409
        charge_in_progress`, which is `retryable`, while a card charge for the
        plan is still with the bank: nothing new was charged, so read the store
        again shortly.
      operationId: reactivateStorePlan
      parameters:
        - name: Idempotency-Key
          in: header
          description: >-
            Required: a call without one is refused with `validation_failed`,
            naming this header. Generate a NEW key for each logical call (a UUID
            is a good one), at most 255 characters, and send the SAME key when
            you retry that call after a timeout or a dropped connection. The
            same key with the same body returns the ORIGINAL response, status
            code included, instead of doing the work again, for 24 hours. The
            same key with a different body is refused with `409
            idempotency_conflict`, and so is a retry that arrives while the
            first request is still being processed (try again shortly). A key
            belongs to your account and to the mode of the API key that sent it
            (live and test keys never meet), so the same text from two accounts,
            or from a live and a test key, is two different keys.
          required: true
          schema:
            type: string
          example: pos-store-41-reactivate-plan-1
        - name: store_id
          in: path
          required: true
          schema:
            type: string
          example: st_4bR8kM2wQ7xT1nV5cZ9pLdYe
      responses:
        '200':
          description: The request succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Store'
              example:
                id: st_4bR8kM2wQ7xT1nV5cZ9pLdYe
                external_id: null
                name: Tortería La Mera Mera
                phone: '+528112345678'
                street: Av. Juan Palomar y Arias 765
                apt_unit: null
                lat: 20.691145
                lng: -103.426885
                pickup_notes: null
                timezone: America/Mexico_City
                default_business_fee_share: 0
                default_fee_split_mode: percent
                default_fee_split_amount_cents: null
                access_tier: api_only
                markup_cents: 0
                billing: partner
                plan:
                  status: active
                  price_cents: 199000
                  currency: MXN
                  interval: month
                  current_period_ends_on: '2026-10-06'
                  next_charge_on: '2026-10-07'
                  canceled_at: null
                  ends_on: null
                  paid_by: partner
                  pending_paid_by: null
                  pending_paid_by_on: null
                  courtesy: false
                test: false
                created_at: '2026-09-07T18:00:00Z'
        '402':
          description: >-
            The prepaid balance does not cover this request
            (`insufficient_balance`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: insufficient_balance
                  message: >-
                    Neither your balance nor your saved card could pay this
                    store's plan: $0.00 available, $2,308.40 due including IVA.
                    Nothing was charged and the plan stays ended.
                  param: null
                  retryable: false
                  doc_url: https://docs.listo.delivery/en/errors/insufficient_balance
                  request_id: 0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
        '403':
          description: >-
            The key is valid and is read-only (`read_only_key`), so it can read
            but not create, change or cancel anything. Use a key issued without
            the read-only setting.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                read_only_key:
                  value:
                    error:
                      code: read_only_key
                      message: >-
                        This API key is read-only. Use a key without the
                        read-only setting to create, change or cancel anything.
                      param: null
                      retryable: false
                      doc_url: https://docs.listo.delivery/en/errors/read_only_key
                      request_id: 0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
        '404':
          description: >-
            No object with that id exists for this key (`not_found`). Another
            account's objects are indistinguishable from ones that never
            existed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: not_found
                  message: No resource with that id exists for this API key.
                  param: null
                  retryable: false
                  doc_url: https://docs.listo.delivery/en/errors/not_found
                  request_id: 0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
        '409':
          description: >-
            The request conflicts with the state of the world. The object is no
            longer in a state that allows this (`not_cancellable`,
            `not_reschedulable`, `not_retriable`, `not_editable`,
            `not_refundable`, `not_reactivatable`, `plan_ended`), the
            cancellation did not go through (`cancel_failed`), the plan's charge
            is still with the bank (`charge_in_progress`), the idempotency key
            was reused with a different body (`idempotency_conflict`, which no
            retry fixes), the idempotency key is still held by the request that
            first sent it (`idempotency_conflict`, and once that request has
            answered the same call with the same key replays its answer), there
            is no webhook URL configured for the key's mode so there is nowhere
            to send (`webhook_endpoint_not_configured`), the event is a courier
            position, which is never sent again (`event_not_resendable`), the
            email already has a Listo account (`account_exists`), the store's
            pickup point is still being moved (`pickup_point_pending`), or no
            courier is free for this trip right now (`no_couriers_available`).
            Worth repeating unchanged after a few minutes: `cancel_failed`,
            `charge_in_progress`, `pickup_point_pending`,
            `no_couriers_available`, and a call refused because its idempotency
            key is still held (repeat it with the same key). Every other refusal
            here needs the request or the object to change first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: charge_in_progress
                  message: >-
                    A card charge for this store's plan is still being processed
                    by the bank. Nothing new was charged. Read the store again
                    shortly.
                  param: null
                  retryable: true
                  doc_url: https://docs.listo.delivery/en/errors/charge_in_progress
                  request_id: 0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
        '422':
          description: >-
            The request was understood and refused. `error.code` says why and
            `error.param` names the offending field when one applies.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: validation_failed
                  message: Idempotency-Key is required.
                  param: Idempotency-Key
                  retryable: false
                  doc_url: https://docs.listo.delivery/en/errors/validation_failed
                  request_id: 0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
      security:
        - BearerAuth: []
components:
  schemas:
    Store:
      type: object
      description: |
        A pickup location. Card readiness is deliberately NOT a field here: it
        is its own endpoint, with its own outstanding-requirements detail.
      properties:
        id:
          type: string
          description: The store id, `st_` prefixed.
          example: st_4bR8kM2wQ7xT1nV5cZ9pLdYe
        external_id:
          type:
            - string
            - 'null'
          description: |
            Your own reference for this location, if you sent one. Unique per
            key world, and filterable on the list, so you can reconcile against
            your own store book without storing our ids.
          example: pos-store-41
        name:
          type: string
          description: >
            The name you gave the store; for a restaurant that connected an

            account with several branches, the business and the branch, the way

            its own customers read it (`Tortería La Mera Mera, Zapopan`). A

            branch whose own name already begins with the business's name, such

            as `Tortería La Mera Mera Centro`, is published as it is. When

            you send a name, a value that begins with the store's business name

            and a comma is read as that business plus the branch, so sending
            back

            the name you read changes nothing.
        phone:
          type: string
          description: E.164, normalized on the way in.
        street:
          type: string
        apt_unit:
          type:
            - string
            - 'null'
        lat:
          type:
            - number
            - 'null'
          format: double
        lng:
          type:
            - number
            - 'null'
          format: double
        pickup_notes:
          type:
            - string
            - 'null'
          description: Standing instructions for the courier collecting here.
        timezone:
          type: string
          description: |
            The IANA zone the store keeps its hours in, normalized to one
            spelling per zone. Derived from the store's coordinates, and
            re-derived whenever the store's position moves. A zone you send
            yourself is used only when the position cannot answer (a store with
            no coordinates, or a position no zone claims), and one that
            disagrees with the position is neither stored nor treated as an
            error.
          example: America/Mexico_City
        default_business_fee_share:
          type: integer
          minimum: 0
          maximum: 100
          description: |
            The share of the delivery fee this store covers by default, as a
            percentage. The rest is charged to the customer. In force only when
            `default_fee_split_mode` is `percent`; in either amount mode this is
            the percentage the store keeps for when it goes back to one.
        default_fee_split_mode:
          type: string
          enum:
            - percent
            - business_amount
            - customer_amount
          description: |
            Which shape this store's default split has, and therefore which of
            the two fields above and below is in force. `percent` splits the fee
            by `default_business_fee_share`; `business_amount` means the store
            covers up to `default_fee_split_amount_cents` of it and the customer
            pays the rest; `customer_amount` is the mirror, the customer paying
            up to that amount and the store covering the rest.

            Read-only. It is set by the restaurant in its own Listo console, and
            sending `default_business_fee_share` on a create or an update moves
            the store back to `percent`.
        default_fee_split_amount_cents:
          type:
            - integer
            - 'null'
          minimum: 0
          description: |
            The amount the two amount modes divide the fee by, in cents. Null in
            `percent` mode, and never null in either amount mode. Read-only.
        access_tier:
          type: string
          enum:
            - api_only
            - wallet
            - full
          description: |
            How much of Listo the store itself uses. An `api_only` store's
            deliveries spend your balance, and a `wallet` or `full` store's
            spend its own; `billing` says which. Writable on create and update,
            and always `api_only` on a test store. A live store of an account
            whose restaurants pay their own Plan Listo cannot be `api_only`: it
            has no console to buy a plan from, so a create or an update that
            asks for it is refused with `422 validation_failed` and
            `param: "access_tier"`.
        markup_cents:
          type: integer
          minimum: 0
          description: |
            Your own markup on every delivery of this store, in cents. It is
            added to the delivery fee, split with the customer like the rest of
            the fee, and credited to your balance when the delivery is
            delivered. Writable on create and update.
        billing:
          type: string
          enum:
            - partner
            - store
          description: |
            Whose balance pays this store's deliveries, read off `access_tier`
            so you never have to derive it: `partner` is yours, `store` is the
            store's own. Read-only. It is not who pays the store's plan, which
            is `plan.paid_by`.
        test:
          type: boolean
        created_at:
          type: string
          format: date-time
        plan:
          description: >
            The store's Plan Listo, or `null` when the store needs no plan: a

            test store, or a live store created before Plan Listo existed, which

            keeps requesting couriers without one. It is also `null` when the

            store pays its own plan (every store of an account whose stores pay

            their own Plan Listo) and the restaurant has not bought one yet: the

            restaurant buys it in its own console, and there is nothing for you

            to pay or to reactivate. It is also `null` for a store of a partner

            on pricing with no Plan Listo, which needs no plan. When it is an

            object every

            key is present, and the ones that do not apply are `null`. See

            [Plan and
            billing](https://docs.listo.delivery/en/guides/plan-and-billing).
          type:
            - object
            - 'null'
          properties:
            status:
              type: string
              enum:
                - active
                - past_due
                - canceling
                - canceled
                - awaiting_payment
              description: >
                `active` is paid up and renews on `next_charge_on` (null on a

                plan that is passing to the restaurant, see `pending_paid_by`).
                `past_due`

                has an unpaid month, and its deliveries are refused with `402

                plan_required` until the charge goes through. `awaiting_payment`

                is a store whose first month your balance and card could not

                cover: it holds no plan yet, `price_cents` is the price it

                will be charged before IVA (the amount needed to pay is that

                plus IVA), every date is null, its deliveries are refused with

                `402 plan_required`, and Listo charges it by itself as soon as

                your balance or card covers it. `canceling` was

                canceled and is served up to the day before `ends_on`.

                `canceled` has ended, and can be restarted.
            price_cents:
              type: integer
              description: |
                What one month of this store's plan costs you, before IVA: your
                price, even while a restaurant that connected to you is still
                paying its own plan. When `paid_by` is `store` because the
                store pays its own plan, it is the price of one `interval` of
                the restaurant's own plan, which is not yours to pay.
              example: 199000
            currency:
              type: string
              example: MXN
            interval:
              type: string
              description: |
                How long one period of this plan is, and the cycle `price_cents`
                is the price of. `month` for every plan you pay. A store that
                pays its own plan reports the interval its restaurant bought,
                so `year` is a restaurant that paid twelve months at once.
              example: month
            current_period_ends_on:
              type:
                - string
                - 'null'
              format: date
              description: |
                The last day of the current period, already paid unless
                `status` is `past_due`.
            next_charge_on:
              type:
                - string
                - 'null'
              format: date
              description: |
                The day you are charged next: the next renewal, or, while a
                restaurant that connected to you still pays its own plan, your
                first charge. Null once the plan is ending or has ended, and
                null while it is `past_due`: the month owed is charged the
                moment your balance or saved card allows, and retried every
                day until it is paid. Null while `courtesy` is true, too. Null
                as well on a plan you pay that is passing to the restaurant
                (`pending_paid_by` is `store`): you pay the period you are in
                and nothing after it. When `paid_by` is `store` because the
                store pays its own plan, it is the day the restaurant's own plan
                renews, not a charge to you.
            canceled_at:
              type:
                - string
                - 'null'
              format: date-time
              description: When you canceled the plan.
            ends_on:
              type:
                - string
                - 'null'
              format: date
              description: >
                The day the plan stops serving the store, which is the day after

                the last day paid, or the day a plan whose charge was pending
                was

                canceled. Null unless the plan is ending or has ended.
            paid_by:
              type: string
              enum:
                - partner
                - store
              description: |
                Who pays this plan: `partner`, which is you, or `store`, the
                restaurant itself. `store` is a restaurant that connected to you
                and still pays its own plan, and every store that pays its own
                Plan Listo because your account is set up that way; none of
                those is yours to pay.
            pending_paid_by:
              type:
                - string
                - 'null'
              description: >
                Who will pay it from `pending_paid_by_on`, when that is about to

                change, or null. `partner` is you: a restaurant that connected
                to

                you and still pays its own plan, until the renewal you pay from.

                `store` is the restaurant: a plan you pay that passes to the

                restaurant at its next renewal because your account is set up so

                that each restaurant pays its own plan. You pay the period you

                are in and nothing after it, so `next_charge_on` is null.
            pending_paid_by_on:
              type:
                - string
                - 'null'
              format: date
              description: |
                The date that change lands (the day the new payer's first
                period starts), or null.
            courtesy:
              type: boolean
              description: |
                `courtesy` is true while Listo waives this store's plan: nothing
                is charged and `next_charge_on` is null.
          required:
            - status
            - price_cents
            - currency
            - interval
            - current_period_ends_on
            - next_charge_on
            - canceled_at
            - ends_on
            - paid_by
            - pending_paid_by
            - pending_paid_by_on
            - courtesy
      required:
        - id
        - external_id
        - name
        - phone
        - street
        - apt_unit
        - lat
        - lng
        - pickup_notes
        - timezone
        - default_business_fee_share
        - default_fee_split_mode
        - default_fee_split_amount_cents
        - access_tier
        - markup_cents
        - billing
        - test
        - created_at
        - plan
    ErrorResponse:
      type: object
      description: The body of every 4xx and 5xx answer on this API.
      properties:
        error:
          $ref: '#/components/schemas/Error'
      required:
        - error
    Error:
      type: object
      description: |
        What every refusal and every failure looks like. `code` is the only
        field to branch on; `message` is for a human reading a log and may
        change wording or language without notice.
      properties:
        code:
          type: string
          description: |
            A stable, machine-readable name for what went wrong, such as
            `validation_failed`, `not_found` or `insufficient_balance`. Branch
            on this and never on `message`. The set grows additively: handle a
            code you do not know as the generic failure its status code says it
            is. Every code has a page under `doc_url`.
          example: validation_failed
        message:
          type: string
          description: |
            A sentence for the person debugging. Free to change wording or
            language, so never match on it.
          example: order_value_cents is required.
        param:
          type:
            - string
            - 'null'
          description: |
            The request field the refusal is about, when one applies, spelled
            as you sent it (`dropoff.street`, `manifest.items`). Null when the
            failure is not about one field.
          example: order_value_cents
        retryable:
          type: boolean
          description: |
            Whether sending the identical request again could succeed. `false`
            means it never will without a change on your side or in the world.
          example: false
        doc_url:
          type: string
          format: uri
          description: The documentation page for this `code`.
          example: https://docs.listo.delivery/en/errors/validation_failed
        request_id:
          type: string
          description: |
            Our id for this request, the same value as the `X-Request-Id`
            response header. Quote it to support, and filter
            `GET /v1/request_logs` by it.
          example: 0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
      required:
        - code
        - message
        - param
        - retryable
        - doc_url
        - request_id
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: >-
        Your API key, sent as `Authorization: Bearer <key>`. Live and test keys
        are separate; the key you send decides which world the call acts in. To
        try calls, use a test key (it starts with `lst_test_`): no real courier
        is sent and no real money moves, but webhooks fire and the
        `dropoff.phone` you give can receive WhatsApp messages, so use your own
        number. A live key (it starts with `lst_live_`) books and charges real
        deliveries.

````

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