> ## 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.

# Update a store

> Updates a store. Send only the fields you are changing. Existing
deliveries are unaffected: the pickup name, street and coordinates are
snapshotted onto each delivery when it is created, so a rename or a move
never rewrites where a past courier actually went.

`phone` is E.164. A Mexican number is +52 followed by its 10 digits (+52 1 followed by 10 is accepted as WhatsApp writes it); a US or Canadian number is +1 followed by its 10 digits; a number from any other country is accepted with 8 to 15 digits in total. Anything else is refused with `validation_failed`.

Moving a store re-derives its `timezone` from its new position, so a
location that moves to another zone is corrected by the same request
that moves it, and a request that does not move the store leaves the
zone alone.

A `timezone` you send follows the create's rule: it is used only when
the store's position cannot answer, never in place of a zone the
position does answer. Sending one that disagrees changes nothing, and it
is not an error.

A `street` that moves the store to another country is refused with
`422 unsupported_country`, exactly as on the create, and the store keeps
the address it had.




## OpenAPI

````yaml /openapi/en.yaml patch /v1/stores/{id}
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/{id}:
    patch:
      tags:
        - Stores
      summary: Update a store
      description: >
        Updates a store. Send only the fields you are changing. Existing

        deliveries are unaffected: the pickup name, street and coordinates are

        snapshotted onto each delivery when it is created, so a rename or a move

        never rewrites where a past courier actually went.


        `phone` is E.164. A Mexican number is +52 followed by its 10 digits (+52
        1 followed by 10 is accepted as WhatsApp writes it); a US or Canadian
        number is +1 followed by its 10 digits; a number from any other country
        is accepted with 8 to 15 digits in total. Anything else is refused with
        `validation_failed`.


        Moving a store re-derives its `timezone` from its new position, so a

        location that moves to another zone is corrected by the same request

        that moves it, and a request that does not move the store leaves the

        zone alone.


        A `timezone` you send follows the create's rule: it is used only when

        the store's position cannot answer, never in place of a zone the

        position does answer. Sending one that disagrees changes nothing, and it

        is not an error.


        A `street` that moves the store to another country is refused with

        `422 unsupported_country`, exactly as on the create, and the store keeps

        the address it had.
      operationId: updateStore
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          example: st_4bR8kM2wQ7xT1nV5cZ9pLdYe
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateStoreRequest'
            example:
              name: Chapalita Sur
              pickup_notes: null
              access_tier: api_only
              markup_cents: 2000
              external_id: pos-store-41
              default_business_fee_share: 60
              phone: '+523398765432'
              street: Av. Juan Palomar y Arias 770
              apt_unit: Local 5
              lat: 20.691445
              lng: -103.426585
              timezone: America/Mexico_City
      responses:
        '200':
          description: The request succeeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Store'
              example:
                id: st_4bR8kM2wQ7xT1nV5cZ9pLdYe
                name: Chapalita
                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
                test: false
                created_at: '2026-07-31T22:15:08Z'
                access_tier: api_only
                markup_cents: 0
                billing: partner
                external_id: null
                default_fee_split_mode: percent
                default_fee_split_amount_cents: null
                plan: null
        '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
        '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: Name can't be blank
                  param: name
                  retryable: false
                  doc_url: https://docs.listo.delivery/en/errors/validation_failed
                  request_id: 0b1f6c9a-7e2d-4c53-9a41-5d8e3f2a6b10
      security:
        - BearerAuth: []
components:
  schemas:
    UpdateStoreRequest:
      type: object
      description: The fields to change. Send only the ones you are changing.
      properties:
        name:
          type: string
        phone:
          type: string
          description: A phone that resolves to an E.164 number.
        street:
          type: string
          description: The full street address, in one string.
        apt_unit:
          type: string
        lat:
          type: number
          format: double
          minimum: -90
          maximum: 90
          description: Latitude. Send it together with `lng`.
        lng:
          type: number
          format: double
          minimum: -180
          maximum: 180
          description: Longitude. Send it together with `lat`.
        pickup_notes:
          type:
            - string
            - 'null'
          maxLength: 280
          description: >-
            Instructions for the courier at the store, up to 280 characters.
            Null clears them.
        timezone:
          type: string
          description: An IANA time zone name.
        default_business_fee_share:
          type: integer
          minimum: 0
          maximum: 100
        access_tier:
          type: string
          enum:
            - api_only
            - wallet
            - full
        markup_cents:
          type: integer
          minimum: 0
          maximum: 1000000
        external_id:
          type:
            - string
            - 'null'
          maxLength: 255
          description: Your own reference for this location. Null clears it.
    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.