Skip to main content
Stateful Square emulation of the v2 REST API at Square version 2026-09-16. It covers payments with the sandbox test nonces, delayed capture, completion, and cancellation; refunds, including partial ones; orders whose totals are computed on the server from line items, taxes, discounts, and service charges; customers; the catalog, with temporary #ids mapped in id_mappings; locations and merchants. Writes take idempotency keys, lists page with cursors, and errors use Square’s errors envelope. It also covers OAuth (authorization code and PKCE flows, refresh, revoke, token status) with scopes checked per endpoint, and webhook subscriptions whose events are signed in x-square-hmacsha256-signature. The tests drive it with the official square SDK (46.0.0) and verify webhook signatures with its WebhooksHelper.verifySignature.

At a glance

Start

Square starts only when named in config or with --service. When it is the only enabled service, it listens on http://localhost:4000. Without config, the seller is Default Test Account with one main location and a default application (sandbox-sq0idb-baseline). Point the SDK at the emulator with baseUrl:

URL mapping

Auth

Requests send Authorization: Bearer <token>.
  • A token the CLI accepts (for example test_token_admin) acts as the first application’s personal access token, with every scope.
  • apps[].personal_access_token acts as that application, with every scope.
  • tokens[] seeds extra tokens. With scopes it behaves like an OAuth access token limited to those scopes; without them it is a personal token.
  • OAuth access tokens carry the scopes the seller approved.
An unknown token answers 401 UNAUTHORIZED. Revoked and expired tokens answer ACCESS_TOKEN_REVOKED and ACCESS_TOKEN_EXPIRED. A token without an endpoint’s scope answers 403 INSUFFICIENT_SCOPES and names the missing scopes.

OAuth

  • GET /oauth2/authorize?client_id=...&scope=PAYMENTS_WRITE+ORDERS_READ&state=... — Authorize: shows a consent page with the requested permissions. Approving redirects to the application’s redirect URL with code, response_type=code, and state; declining sends error=access_denied.
  • POST /oauth2/token — Obtain token: with grant_type=authorization_code, client_id, client_secret, and code. Public clients send code_verifier instead of the secret (PKCE, S256 or plain). Access tokens last 30 days, or 24 hours with short_lived: true.
  • The same call with grant_type=refresh_token refreshes a token. In the code flow the refresh token stays the same; in the PKCE flow it is single use, rotated on every refresh, and expires after 90 days. scopes narrows a refresh.
  • POST /oauth2/revoke — Revoke token: with Authorization: Client <secret> and an access_token or merchant_id, it revokes the grant (revoke_only_access_token keeps the refresh token) and sends oauth.authorization.revoked.
  • POST /oauth2/token/status — Retrieve token status: reports a token’s scopes, expiry, client, and merchant.

Requests

Square-Version

The Square-Version header is optional. Without it, requests are answered at 2026-09-16. A value that is not YYYY-MM-DD answers 400 INVALID_SQUARE_VERSION_FORMAT. Webhook subscriptions keep the version they were created with.

Idempotency

Writes take idempotency_key in the body. The same key with the same body returns the first answer, including a decline; with a different body it answers 400 IDEMPOTENCY_KEY_REUSED. Keys are scoped to the application and the endpoint.

Pagination

List and search endpoints return cursor when there is more. A cursor is tied to the filters it came from; any other value answers INVALID_CURSOR.

Payments

  • POST /v2/payments — Create payment: source_id, idempotency_key, amount_money, and optionally tip_money, app_fee_money, autocomplete, delay_duration, delay_action, order_id, customer_id, location_id, reference_id, note, and buyer_email_address
  • GET /v2/payments — List payments: filters by begin_time, end_time, sort_order, location_id, total, last_4, and card_brand, and pages with cursor and limit
  • GET /v2/payments/:id — Get payment
  • PUT /v2/payments/:id — Update payment: amounts and tip of an APPROVED payment, with version_token
  • POST /v2/payments/:id/complete — Complete payment
  • POST /v2/payments/:id/cancel — Cancel payment
  • POST /v2/payments/cancel — Cancel payment by idempotency key
With autocomplete: false a card payment stays APPROVED until it is completed or canceled. After delay_duration (default PT168H) the delay_action (CANCEL by default, or COMPLETE) is applied. Completing or canceling a payment that is not APPROVED answers DELAYED_TRANSACTION_CAPTURED, DELAYED_TRANSACTION_CANCELED, or DELAYED_TRANSACTION_FAILED. A payment against an order must match the order’s amount due (PAYMENT_AMOUNT_MISMATCH) and location (LOCATION_MISMATCH). When the order is fully paid it gets a tender and closes, unless it has fulfillments.

Test values

A declined payment is stored as FAILED and answered with 400 PAYMENT_METHOD_ERROR, the decline code, and the payment. An unknown nonce answers 404 NOT_FOUND. A card ending in 2222 is rated MODERATE risk and 3333 HIGH.

Refunds

  • POST /v2/refunds — Refund payment: all or part of a completed payment
  • GET /v2/refunds — List payment refunds
  • GET /v2/refunds/:id — Get payment refund
Refunding more than what is left answers REFUND_AMOUNT_INVALID, an APPROVED payment REFUND_ERROR_PAYMENT_NEEDS_COMPLETION, and a canceled or failed payment PAYMENT_NOT_REFUNDABLE. Card refunds start PENDING and complete after refund_delay_ms (default one second); cash and external refunds complete at once. The payment’s refunded_money and refund_ids update straight away.

Orders

  • POST /v2/orders — Create order: line items (catalog variations or ad hoc), taxes (ADDITIVE or INCLUSIVE), discounts (FIXED_PERCENTAGE, FIXED_AMOUNT, at order or line item scope), service charges, and fulfillments. Every total is computed on the server
  • POST /v2/orders/calculate — Calculate order: prices an order without saving it
  • POST /v2/orders/clone — Clone order: copies one into a new DRAFT
  • GET /v2/orders/:id — Retrieve order
  • POST /v2/orders/batch-retrieve — Batch retrieve orders
  • PUT /v2/orders/:id — Update order: with the current version (VERSION_MISMATCH otherwise): a sparse update that merges lists by uid and honors fields_to_clear
  • POST /v2/orders/search — Search orders: location_ids, state_filter, date_time_filter, customer_filter, source_filter, fulfillment_filter, and sort (the sort field must match the date filter field), return_entries, cursor, and limit
  • POST /v2/orders/:id/pay — Pay order: payment_ids that cover the amount due

Customers

  • GET /v2/customers — List customers
  • POST /v2/customers — Create customer
  • DELETE /v2/customers/:id — Delete customer: the same path takes GET and PUT, all with version checks
  • POST /v2/customers/bulk-create — Bulk create customers: also bulk-retrieve, bulk-update, and bulk-delete
  • POST /v2/customers/search — Search customers: email_address, phone_number, reference_id, created_at, updated_at, and creation_source filters and a sort
Creating a customer needs a name, company, email, or phone number.

Catalog

  • POST /v2/catalog/object — Upsert catalog object
  • POST /v2/catalog/batch-upsert — Batch upsert catalog objects
  • GET /v2/catalog/list — List catalog: filtered by types
  • GET /v2/catalog/object/:id — Retrieve catalog object: include_related_objects
  • POST /v2/catalog/batch-retrieve — Batch retrieve catalog objects
  • DELETE /v2/catalog/object/:id — Delete catalog object: deleting an item deletes its variations
  • POST /v2/catalog/batch-delete — Batch delete catalog objects: deleting an item deletes its variations
  • POST /v2/catalog/search — Search catalog objects: exact_query, set_query, prefix_query, range_query, text_query, items_for_tax_query, and sorted_attribute_query
  • POST /v2/catalog/search-catalog-items — Search catalog items
  • POST /v2/catalog/update-item-taxes — Update item taxes
  • GET /v2/catalog/info — Catalog info
Upserts take temporary ids such as #latte, which may refer to each other; the answer maps them in id_mappings. Updating an object needs its current version. Items, variations, categories, taxes, discounts, modifier lists, and modifiers are stored.

Locations and merchants

  • POST /v2/locations — Create location: GET on the same path lists them
  • PUT /v2/locations/:id — Update location: GET on the same path retrieves one
  • GET /v2/merchants — List merchants
  • GET /v2/merchants/:id — Retrieve merchant: me works
main stands for the main location, the first one in config. The merchants endpoints describe the seller.

Webhooks

  • GET /v2/webhooks/event-types — List webhook event types
  • POST /v2/webhooks/subscriptions — Create webhook subscription: GET on the same path lists them
  • DELETE /v2/webhooks/subscriptions/:id — Delete webhook subscription: the same path takes GET and PUT
  • POST /v2/webhooks/subscriptions/:id/signature-key — Update webhook signature key: rotates the key
  • POST /v2/webhooks/subscriptions/:id/test — Test webhook subscription: sends a test event and returns the receiver’s answer
Subscription endpoints need a personal access token, as on Square. The signature key is returned on create and rotation only. Events: payment.created, payment.updated, refund.created, refund.updated, order.created, order.updated, customer.created, customer.updated, customer.deleted, catalog.version.updated, location.created, location.updated, and oauth.authorization.revoked. Each is posted as { merchant_id, type, event_id, created_at, data: { type, id, object } } with x-square-hmacsha256-signature (base64 HMAC-SHA256 of the notification URL followed by the raw body, keyed with the signature key), so WebhooksHelper.verifySignature accepts it:
A subscription hears an application’s events when the application owns it and holds a personal token or an OAuth token with the event’s read scope. A failed delivery is retried seven times over about two minutes with square-retry-number and square-retry-reason.

Seed config

  • name: the first location is the main one
catalog entries use Square’s upsert shape, so temporary ids may refer to each other. A webhook without event_types hears every event Baseline sends; without signature_key one is generated.

Inspector

GET /_inspector shows payments with their refunds, orders with their totals, customers, catalog objects, locations, and applications with their tokens and webhook subscriptions. Each payment’s receipt_url opens a receipt page at /_square/receipts/:id.

Limits

Terminal, invoices, subscriptions, gift cards, loyalty, bookings, team, labor, inventory counts, disputes, payouts, cards on file (/v2/cards), and the v1 API. Other /v2 paths answer 404 NOT_FOUND.

Conformance

See CONFORMANCE.md for every provider.