# Quick start

Source: https://docs.fiest.io/api/quickstart

This guide is the shortest safe path for implementing an approved Partner API
sandbox connection. It uses the public Fiest Partner API contract and a client
registration supplied privately by Fiest.

<Callout type="info" title="Sandbox preview">
  The sandbox is available to approved partners and contains synthetic test
  data. Production uses a separate, explicitly approved client registration.
  Do not substitute production URLs or credentials while following this
  sandbox guide.
</Callout>

## Registration values [#registration-values]

| Setting          | Value                                |
| ---------------- | ------------------------------------ |
| OAuth issuer     | `https://auth-sandbox.fiest.io`      |
| Client ID        | Your Fiest-issued sandbox client ID  |
| Client type      | Public OAuth client                  |
| Client secret    | None                                 |
| Redirect URI     | Your exact registered HTTPS callback |
| API resource     | `https://api-sandbox.fiest.io`       |
| OpenAPI contract | `https://docs.fiest.io/openapi.yaml` |

Fiest shares the client ID and confirms the exact redirect URI directly with
each approved partner. Do not copy values from another integration.

These sandbox endpoints do not grant access to Fiest's internal development or
staging systems.

<Callout type="info" title="Why there is no client secret">
  This registration is a public OAuth client protected by PKCE S256. A static
  secret embedded in a distributed client cannot be kept confidential. Security
  instead relies on the exact redirect allowlist, high-entropy state, PKCE,
  one-time short-lived authorization codes, audience binding, and rotating
  refresh tokens.
</Callout>

Request these scopes:

```text
offline_access
fiest.restaurant.read
fiest.accounting.read
```

Request `fiest.organization.read` only when Fiest has explicitly approved a
Management organization integration. The initial partner sandbox flow should
use one restaurant and the smallest scope set above.

Use [OAuth discovery](https://auth-sandbox.fiest.io/.well-known/openid-configuration)
instead of hardcoding authorization, token, JWKS, or revocation endpoints.
Every authorization and token request must use the exact API resource above.

## Complete sandbox authorization [#complete-sandbox-authorization]

The partner starts the connection from its own product and redirects the user
to `auth-sandbox.fiest.io`. The user does not need to visit the production Fiest
Dashboard first.

For the initial sandbox test:

1. Expand **Use an email code instead** on the Fiest authorization page.
2. Enter the pre-provisioned sandbox account email supplied by Fiest.
3. Enter the six-digit code delivered directly to that inbox. The partner
   application and coding agent must never request or receive the code.
4. Choose one reviewed test restaurant and approve the displayed read-only
   permissions.
5. Fiest redirects the browser to the exact registered partner callback.

Email verification only proves an existing Fiest identity; it never creates an
account. Organization-wide authorization is introduced separately through the
Fiest Management flow.

## Token lifecycle [#token-lifecycle]

The token response is authoritative. Read `expires_in`, store the resulting
expiry with the connection, and do not assume that a token remains valid just
because it has not reached its expected expiry. A Fiest owner, administrator,
or security control can revoke access earlier.

| Credential         | Current lifetime      | Required client behavior                                                                                                                                                 |
| ------------------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Authorization code | 5 minutes, single-use | Exchange it promptly and exactly once with the original PKCE verifier. Never persist it as a connection credential.                                                      |
| Access token       | 1 hour                | Send it only to the exact API resource for which it was issued. Use the returned `expires_in` value and obtain a replacement with the current refresh token when needed. |
| Refresh token      | 90 days, rotating     | Store it encrypted. Each successful refresh consumes it and returns a new refresh token with a new bounded 90-day lifetime. Replace the stored token atomically.         |

Refresh-token replay invalidates the associated refresh family. If refresh is
rejected definitively, or no valid refresh token remains, delete unusable token
material and move the connection to an explicit **Reconnect Fiest** state. Do
not retry a consumed refresh token.

Revocation is independent of these maximum lifetimes. A revoked connection,
removed membership, disabled client, or invalid authorization boundary can
make otherwise unexpired tokens unusable immediately. Reconnecting starts a
new OAuth authorization; it must not reuse an earlier authorization code,
`state`, PKCE verifier, or refresh token.

## Implementation sequence [#implementation-sequence]

1. Generate a high-entropy OAuth `state` and PKCE verifier for each connection
   attempt.
2. Store both server-side with a short expiry and send only the PKCE `S256`
   challenge to Fiest.
3. Redirect the user to the discovered authorization endpoint with the exact
   client ID, redirect URI, resource, and scopes.
4. Handle both successful `code` callbacks and OAuth `error` callbacks.
5. Consume `state` and the authorization code once, then exchange the code
   with the original PKCE verifier.
6. Encrypt access and refresh tokens in durable server-side storage.
7. Record the access-token expiry from `expires_in`. Replace rotating refresh
   tokens atomically. A replay or definitive refresh failure must move the
   connection into a clear reconnect state.
8. Discover the current authorization boundary with
   [`GET /v1/restaurants`](/api/reference/list-restaurants). Never infer or
   accept a restaurant outside that response.
9. Read the approved restaurant profile or
   [accounting summary](/api/reference/get-accounting-summary) using only the
   published operations.

<Callout type="warn" title="Keep credentials out of the browser and logs">
  Never render or log access tokens, refresh tokens, authorization codes,
  cookies, PKCE material, customer records, or financial payloads. Retain only
  the safe `request_id` when support correlation is needed.
</Callout>

## Prompt for a coding agent [#prompt-for-a-coding-agent]

Copy the prompt below into Claude Code, Codex, Cursor, or another coding agent.
Give the agent this page and the public OpenAPI contract as its authoritative
inputs.

```text title="AI agent quick setup"
Implement my approved sandbox integration with the Fiest Partner API.
Treat this quick-start page and
https://docs.fiest.io/openapi.yaml as the only authoritative contracts.

Build a server-side OAuth 2.1 authorization-code client with PKCE S256:
- issuer: https://auth-sandbox.fiest.io
- client ID: <insert the client ID supplied privately by Fiest>
- redirect URI: <insert the exact HTTPS callback registered with Fiest>
- resource/audience: https://api-sandbox.fiest.io
- scopes: offline_access fiest.restaurant.read fiest.accounting.read
- client type: public; do not invent or request a client secret

Use discovery rather than hardcoding authorization, token, JWKS, or revocation
endpoints. Generate high-entropy state and a PKCE verifier per attempt, store
them server-side with a short expiry, consume state once, and exchange each
authorization code once with the original verifier and exact resource.

Store access and rotating refresh tokens encrypted in durable server-side
storage. Treat access tokens as one-hour credentials while honoring the token
response's `expires_in` value. Replace the refresh token atomically, reject
replay, and move the connection to an explicit reconnect state when refresh
fails definitively. A successful refresh returns a new rotating refresh token
with a new bounded 90-day lifetime.
Never render or log tokens, authorization codes, PKCE material, cookies,
customer data, or financial payloads.

Implement the callback's OAuth success and error paths, then implement only
the public GET operations in the supplied OpenAPI contract. Discover the
authorized restaurant ID from GET /v1/restaurants; never accept or infer a
restaurant outside that grant. Branch on stable API error codes and retain
request_id for redacted support logs.

Add automated tests for state expiry and reuse, wrong PKCE verifier, callback
errors, one-time code exchange, exact resource/audience, refresh rotation and
replay, reconnect state, out-of-grant restaurant denial, invalid dates, 429
Retry-After handling, and redaction. Do not request write scopes, use an MCP
token, add a generic HTTP proxy, or point any test at production.

Before claiming success, report:
1. the files changed;
2. the durable state/token storage model;
3. all tests and type checks run;
4. any assumption not explicitly supported by the two provided contracts; and
5. what remains for a real browser authorization.

Do not describe type safety as verified if the strict typecheck did not run.
Report the exact blocker instead.
```

## Acceptance checklist [#acceptance-checklist]

Before the sandbox connection is considered complete:

* the registered callback handles OAuth success and denial without returning
  `404`;
* the authorized restaurant list contains only the reviewed restaurant or
  organization boundary;
* an out-of-grant restaurant returns the documented safe error;
* two complete accounting periods reconcile;
* refresh rotation succeeds once and replay of the previous token fails;
* revocation denies the next API request and reconnect restores the same
  reviewed boundary;
* `429` handling respects `Retry-After`; and
* operational logs contain request IDs but no credentials or financial data.

See [stable API errors](/api/reference/errors) before adding retry behavior.