# auth.md

Agent authentication guide for **Lokand** (lokand.com) — the restaurant menu
and website platform. This document tells AI agents how to discover, register
for, and use authenticated access to Lokand's APIs.

## Services

| Surface | Base URL | Auth |
| --- | --- | --- |
| Public read API (menus, business profiles) | `https://api.lokand.com/public/v1` | None (rate-limited: 60 req/min/IP) |
| **Agent operations API** (create + manage restaurants, billing) | `https://api.lokand.com/agent/v1` | OAuth 2.0 scopes (below); reads are open |
| MCP server (same operations as MCP tools) | `https://api.lokand.com/mcp` | Every tool is listed; anonymous callers can run the read tools, and a protected call answers `401`/`403` with a `WWW-Authenticate` challenge naming the scopes to request |
| Owner REST API (dashboard) | `https://api.lokand.com/api/*` | Better Auth sessions |

If you only need to read a restaurant's public menu, use the public API or the
MCP server — no registration required. OpenAPI descriptions:
`https://api.lokand.com/public/v1/openapi.json` (public reads) and
`https://api.lokand.com/agent/v1/openapi.json` (agent operations).

## Agent-native onboarding (no human login required)

Agents can take a restaurant from nothing to a live website — and hand
ownership to the customer — without the customer ever using the dashboard:

1. Register a client once (`POST /api/auth/oauth2/register`, below).
2. `POST /agent/v1/ops/create_restaurant` with your `client_id` +
   `client_secret`, the restaurant's name, (recommended) the customer's
   email — and, ideally, the FULL `menu` inline plus `hours`/`theme`: one
   call then yields a complete live website. You get back: working OAuth
   tokens, the live `site_url`, and a `claim_url`.
3. Refine with the ops API or MCP tools (`create_full_menu`, `add_item`,
   `import_menu` from an existing website URL, `set_theme`,
   `publish_site`, …). Call `get_started` for machine-readable recipes.
   Add allergen/dietary labels with `dietary_info` on the item ops, price a
   dish sold in several forms with `variations` (one item, one entry per
   variation — sizes, volumes, portions — each with its own absolute price;
   never one item per variation), set how the menu presents itself with
   `update_business {menu_options}`, and — if the restaurant's country
   matters — `apply_country_settings` plus `check_compliance` for advisory
   guidance with sources.
   The website is a document of pages → sections → blocks (with settings)
   on top of a theme: call `get_site` first — `document` holds the ids and
   `theme` describes every section and block type the theme offers (their
   settings and presets), the theme settings and style presets. Change it
   with `update_theme_settings`, `apply_style_preset`, `set_theme` (system
   theme: `lokand-base`, `lokand-one`), `apply_template`,
   `set_page`/`add_page`/`remove_page`/`set_nav_links`,
   `add_section`/`update_section`/`move_section`/`remove_section`,
   `add_block`/`update_block`/`move_block`/`remove_block` (blocks nest with
   `parent_block_id`, so new sections can be composed from heading, text,
   button, image, spacer and group blocks), `set_featured_items`, or
   `make_menu_only` for a menu-only site. Settings merge (JSON merge patch),
   every change is validated against the theme before saving, and
   `revert_last_site_change` undoes the last one.
   The footer's "Powered by Lokand" credit is the owner's choice on every
   plan: `update_theme_settings {settings: {show_lokand_credit: false}}`
   removes it and `null` brings it back.
   Custom theme code (Pro plan): `theme_pull {theme_id: "lokand-base"}` for
   a starting point, edit the Liquid, `theme_upload` (validated and compiled
   at once — a 422 lists every `path: message`), poll `theme_get` until
   `ready`, then `theme_publish`; `theme_write_files` changes single files
   (path → text, or null to delete, or find/replace `edits`) as the theme's next version — on the
   live theme it stores a new copy that goes live once stored, keeping the
   previous version unpublished for rollback;
   `theme_list`, `theme_unpublish`, `theme_delete`; every custom theme is a Git repo — `theme_history`,
   `theme_rollback`, and `theme_git_access` for plain `git push` workflows (pushes to `main` compile on their own; `theme_sync` applies one immediately);
   `get_storefront_context` returns the exact data a theme renders with. Menu structure:
   `rename_category`, `reorder_categories`, `delete_category`,
   `reorder_items`, `update_menu`, `delete_menu`; loyalty card:
   `update_loyalty_card` (Pro). Chains (franchise plan): a brand owner
   makes one location's site the shared template with
   `publish_brand_template`, and each location opts in with
   `set_brand_link {linked}`; hours, address, menus and ordering stay
   per location.
4. Give the customer the `claim_url` (we also email it when you provided
   their address). They sign up on that page — free — and choose whether to
   keep letting you manage the restaurant.
5. Poll `claim_status`: once claimed with consent, you receive fresh tokens
   scoped to the real owner (your pre-claim tokens are revoked at claim).
6. Upgrades: `create_checkout_link` returns a Stripe-hosted payment URL to
   hand to the customer — you never touch payment credentials. Pass
   `customer_country` so pricing is quoted in their region.

Unclaimed restaurants are unpublished after 30 days and deleted after 60.

## Discovery

- OIDC: `https://api.lokand.com/.well-known/openid-configuration`
- OAuth 2.0 (with `agent_auth` block): `https://api.lokand.com/.well-known/oauth-authorization-server`
- Protected resource metadata (RFC 9728): `https://api.lokand.com/.well-known/oauth-protected-resource`
- Mirrors of all three are served from `https://lokand.com/.well-known/`.

Issuer: `https://api.lokand.com`. JWKS: `https://api.lokand.com/api/auth/jwks`.

## Register (RFC 7591 dynamic client registration)

Agents self-register — no manual approval step:

```
POST https://api.lokand.com/api/auth/oauth2/register
Content-Type: application/json

{
  "client_name": "your-agent-name",
  "redirect_uris": ["https://your-agent.example/callback"]
}
```

The response contains `client_id` and `client_secret`. Store them securely.
Registration is rate-limited (20 registrations/hour/IP) — register once and
reuse the credentials.

**Native apps and CLIs** (RFC 8252) register as a public client: send
`"token_endpoint_auth_method": "none"` with loopback-only redirect URIs
(`http://127.0.0.1:<port>/callback`). No secret is issued; the token
exchange is authenticated by the PKCE `code_verifier` instead. Redirect URIs
match exactly, port included — register the port you listen on.

## Authorize (authorization code + PKCE)

1. Send the user to
   `https://api.lokand.com/api/auth/oauth2/authorize?response_type=code&client_id=...&redirect_uri=...&scope=openid+profile+email+menu:read&code_challenge=...&code_challenge_method=S256&state=...`
2. The user signs in at the Lokand dashboard and approves the requested scopes.
3. Exchange the returned `code` at `https://api.lokand.com/api/auth/oauth2/token`
   (auth: `client_secret_basic`, `client_secret_post`, or `none` + `code_verifier`
   for public clients; grant types: `authorization_code`, `refresh_token`).

### Scopes

| Scope | Grants |
| --- | --- |
| `openid`, `profile`, `email` | Standard OIDC identity claims |
| `offline_access` | Refresh tokens |
| `menu:read` | Read managed businesses, menus (including unpublished) and the site document |
| `menu:write` | Create/edit/reorder/delete menus, categories, items; AI menu import |
| `business:write` | Update profile/hours/contact, edit the site (theme settings, pages, sections, blocks), publish, loyalty card |
| `billing:manage` | Create Stripe checkout links, read billing state |
| `onboarding:create` | Reserved for restaurant creation flows |
| `theme:read` | List and download custom theme code (`theme_list`, `theme_get`, `theme_pull`) |
| `theme:write` | Upload, edit, publish, unpublish and delete custom theme code (uploads and publishing need the Pro plan) |

## Use tokens

Send access tokens in the `Authorization` header only:

```
Authorization: Bearer <access_token>
```

Bearer-in-query or bearer-in-body are not supported
(`bearer_methods_supported: ["header"]`).

## Refresh and revocation

Refresh tokens rotate: each refresh returns a new refresh token and retires
the one you sent. Revoke a grant (RFC 7009) by POSTing `token` (access or
refresh) plus your client authentication to
`https://api.lokand.com/api/auth/oauth2/revoke`; either token ends the whole
grant. Users can also revoke consent from their Lokand dashboard.

## MCP connector clients

Claude, ChatGPT and other MCP clients connect to `https://api.lokand.com/mcp`
with no setup: discovery starts at
`https://api.lokand.com/.well-known/oauth-protected-resource/mcp`, clients
self-register via dynamic client registration (public clients with PKCE are
accepted on HTTPS and loopback redirects), and sign-in is requested only when
a tool needs it.

## More for agents

- Agent skills index: `https://lokand.com/.well-known/agent-skills/index.json`
- MCP server card: `https://lokand.com/.well-known/mcp/server-card.json`
- API catalog (RFC 9727): `https://lokand.com/.well-known/api-catalog`
- ARD capability manifest: `https://lokand.com/.well-known/ai-catalog.json`
- Personal Agent Protocol manifest: `https://lokand.com/.well-known/poppy.json`
- Site overview for LLMs: `https://lokand.com/llms.txt`

Questions: `hello@lokand.com`.
