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

# Connect an app (OAuth)

> Let a restaurant connect your application with a “Connect EatNow” button: OAuth 2 authorization code with PKCE.

Instead of asking a restaurant to create an API key and paste it into your
product, your application can send the user to EatNow, let them pick a
restaurant and approve the permissions, and receive a token. EatNow implements
the OAuth 2 **authorization code** grant ([RFC 6749](https://www.rfc-editor.org/rfc/rfc6749))
with **PKCE S256** ([RFC 7636](https://www.rfc-editor.org/rfc/rfc7636)), mandatory.

The access token you receive **is a partner API key** for one restaurant: it
works with every [Partner API](/api-reference/introduction) route allowed by its
scopes, exactly like a key created by hand. It does not expire and there is no
refresh token; it stops working when it is revoked.

## Register your application

Applications are registered by EatNow. Send us your application name, its
logo, the **redirect URIs** (compared character for character: scheme, host,
port, path and query) and the scopes you need. You receive a `client_id` and a
`client_secret`, shown once: keep the secret on your server only.

<Note>
  An application published by EatNow (such as ClubNow) works on every
  restaurant. A third-party application also requires the restaurant's API
  option, like a hand-made key.
</Note>

## 1. Send the user to the consent page

Generate a `code_verifier` (43 to 128 characters among `A-Z a-z 0-9 - . _ ~`)
and a random `state`, keep both in the user's session, then redirect the browser
to:

```text theme={null}
https://app.eat-now.io/oauth/authorize
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fyour-app.example%2Foauth%2Fcallback
  &response_type=code
  &scope=WEBHOOKS_WRITE%20RESERVATIONS_READ
  &state=RANDOM_STATE
  &code_challenge=BASE64URL_SHA256_OF_VERIFIER
  &code_challenge_method=S256
```

| Parameter | Required | Value |
| - | - | - |
| `client_id` | Yes | Your application identifier. |
| `redirect_uri` | Yes | One of your registered redirect URIs, exactly. |
| `response_type` | Yes | `code`. |
| `scope` | Yes | [Scope](/api-reference/authentication#scopes) names separated by spaces, among those allowed. |
| `state` | Yes | Opaque value returned unchanged. Check it on return to block cross-site request forgery. |
| `code_challenge` | Yes | `BASE64URL(SHA256(code_verifier))`, without padding (43 characters). |
| `code_challenge_method` | Yes | `S256`. `plain` is refused. |
| `restaurant_hint` | No | Restaurant id or slug to preselect, when the user may connect it. |

```bash theme={null}
# A verifier and its challenge
CODE_VERIFIER=$(openssl rand -base64 48 | tr '+/' '-_' | tr -d '=\n')
CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" | openssl dgst -sha256 -binary \
  | openssl base64 | tr '+/' '-_' | tr -d '=\n')
```

The user signs in if needed, sees your application and the permissions it asks
for, chooses a restaurant among those where they may manage integrations, then
clicks **Allow** or **Deny**.

## 2. Handle the return

EatNow redirects the browser to your `redirect_uri` with `state` and either a
`code`, or an `error`:

| `error` | Meaning |
| - | - |
| `access_denied` | The user clicked **Deny**. |
| `invalid_scope` | A scope is unknown or not allowed for your application. |
| `invalid_request` | `state` or PKCE missing, `plain` method, or repeated parameter. |
| `unsupported_response_type` | `response_type` is not `code`. |

An unknown or disabled `client_id`, or a `redirect_uri` that is not registered,
is never redirected: the user sees the error on EatNow.

The code is valid **5 minutes** and **once**.

## 3. Exchange the code for a token

From your server, `POST https://app.eat-now.io/api/oauth/token` with
`application/x-www-form-urlencoded` (JSON is also accepted). Authenticate the
client with HTTP Basic (`client_id:client_secret`, each form-encoded) **or**
with `client_id` and `client_secret` in the body, not both.

```bash theme={null}
curl https://app.eat-now.io/api/oauth/token \
  -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code=THE_CODE \
  --data-urlencode redirect_uri=https://your-app.example/oauth/callback \
  -d code_verifier="$CODE_VERIFIER"
```

```json theme={null}
{
  "access_token": "eat_partner_v1_3f2a…",
  "token_type": "Bearer",
  "scope": "RESERVATIONS_READ WEBHOOKS_WRITE",
  "restaurant": {
    "id": "cm1restaurant0000abcd",
    "name": "Le Bistrot",
    "slug": "le-bistrot"
  }
}
```

The response carries `Cache-Control: no-store`. Store the token server-side,
with the restaurant id.

| HTTP | `error` | Cause |
| - | - | - |
| 400 | `invalid_request` | Missing or repeated parameter, two client authentication methods, unreadable body. |
| 401 | `invalid_client` | Unknown or disabled client, wrong secret. |
| 400 | `invalid_grant` | Unknown, expired or already used code; `redirect_uri` or `code_verifier` that does not match. |
| 400 | `unsupported_grant_type` | `grant_type` is not `authorization_code`. |
| 429 | — | Rate limit (60 requests per minute per IP). Back off. |

<Warning>
  A code presented a second time is refused **and the token it issued is
  revoked**. Never retry an exchange that may have succeeded: if the response
  was lost, send the user through the consent page again.
</Warning>

## 4. Call the Partner API

```bash theme={null}
curl https://app.eat-now.io/api/partner/v1/me \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

With `WEBHOOKS_WRITE`, subscribe an HTTPS URL to events with
`POST /api/partner/v1/webhook-endpoints`. The signing secret is returned only
in that response. Each event type needs the matching read scope, and contact
details are included in payloads only with `RESERVATIONS_READ_SENSITIVE`. See
[Manage endpoints through the API](/api-reference/webhooks#manage-endpoints-through-the-api).

## Connection lifecycle

* **Reconnection.** A new authorization for the same restaurant does not
  revoke the previous token: once the new connection is in place, revoke the
  previous token with `POST /api/oauth/revoke` (below). At most 3 tokens stay
  active per restaurant and application: issuing a fourth revokes the oldest
  and deletes the webhook endpoints it created.
* **Disconnection by the restaurant.** Settings › Integrations › Connected apps
  lists the connected applications; **Disconnect** revokes every active token
  of the application on that restaurant and deletes their webhook endpoints. Your calls then return `401 INVALID_AUTHENTICATION`:
  ask the user to connect again.
* **Disconnection by your application.** `POST /api/oauth/revoke`
  ([RFC 7009](https://www.rfc-editor.org/rfc/rfc7009)) with `token` and the same
  client authentication as the token endpoint. The answer is `200` even for an
  unknown or already revoked token.

```bash theme={null}
curl https://app.eat-now.io/api/oauth/revoke \
  -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
  -d token="$ACCESS_TOKEN"
```
