Getting started with the REST API
A public REST surface for what you run alongside Next Restaurant: a kiosk, a booking widget, a mobile app, a delivery or accounting integration, or a sync into another system.
It is off by default. Public endpoints are reachable without an admin session, so they are opt-in rather than opt-out.
What it covers:
| Group | You can |
|---|---|
| Catalogue | Read locations and each location's live menu |
| Orders | Place orders, read them back, list them for syncing |
| Reservations | Read bookable slots, book a table, change or cancel a booking |
| Menu & stock | 86 an item or change its price at a location, set stock counts, 86 an ingredient |
| Reporting | Read customers, payments, refunds, the sales summary and timesheets |
It does not take payment, and it is not a remote-administration interface: settings, staff and the menu structure are managed in the admin.
Two ways in
There are two transports. They take the same keys with the same permissions.
| REST (recommended) | Query-string (original) | |
|---|---|---|
| Base | /api/index.php/v1/nextrestaurant |
/index.php?option=com_nextrestaurant&format=json |
| Style | Resource paths and HTTP verbs | One path, operation chosen by task= |
| Covers | Everything on this page | Locations, menu and placing an order only |
| Types | Real arrays, objects and numbers | Some fields are JSON-encoded strings |
| OpenAPI | Fully described | Cannot be described — see below |
| Status | Current | Supported, frozen |
Start with REST. It is the surface the OpenAPI document describes, so you can generate a client, load it into Swagger UI or Postman, and have your tooling validate responses for you.
The query-string lane still works exactly as it always has and is not going away, but it gains no
new endpoints. It cannot be expressed in OpenAPI at all: the specification keys every operation on
a path plus a method, and two GETs on /index.php are not expressible. That limitation is
the reason the REST routes exist.
Enabling it
- Go to Apps and turn on the API app.
- Go to Configuration → REST API and turn on Enable public API. Press Save.
- Under API keys, press New key and create a key for your integration (see below).
- Copy the key immediately. It is shown once.
There are two switches and both must be on. The API app under Apps is the master shutoff; Enable public API is the day-to-day one. Turning either off refuses every request, whoever holds a key. See Troubleshooting for what each one does to webhooks.
Each group of endpoints also needs its own app: reservation endpoints need the Reservations
app, stock endpoints the Inventory app, and so on. An endpoint whose app is off answers
403 MODULE_DISABLED and names the app.
Before 6.1 this screen was Integrations. The API and webhooks now have a screen each: Configuration → REST API and Configuration → Webhooks. Old bookmarks to Integrations open the REST API screen.
API keys

Give every integration its own key, with only the permissions it needs. A booking widget needs to read availability and create bookings; it has no business reading payroll. If a key leaks, you revoke that one key and nothing else stops working.
Creating a key. Press New key, give it a name that says which integration uses it (for example "Booking widget"), and tick its permissions in the grid. Each row is one area, with a Read and a Change column:
| Area | Read | Change |
|---|---|---|
| Locations & menu | menu:read |
menu:write |
| Stock levels | inventory:read |
inventory:write |
| Orders | orders:read |
orders:write |
| Reservations | reservations:read |
reservations:write |
| Customers | customers:read |
— |
| Payments & refunds | payments:read |
— |
| Sales reports | reports:read |
— |
| Timesheets | staff:read |
— |
The scope names are what the API reports. A request a key lacks the permission for is refused with
403 INSUFFICIENT_SCOPE, and meta.required_scopes names what was missing. A Change
permission does not include Read: tick both when an integration needs both.
The key is shown exactly once. Only a SHA-256 hash is stored, so it cannot be recovered. If you
lose it, create a new key and revoke the old one. Keys start with nrk_, so a leaked key is easy to
recognise in logs and code.
Whose key it is. A key belongs to the person who created it and acts as that Joomla user. It
can never do more than that person's Next Restaurant role allows, and this is checked on every
request: take a permission away from the person and their keys lose it at once (403 INSUFFICIENT_PERMISSION). The create dialog only offers the permissions your role has. If the
person's account is blocked, not yet activated, waiting for a password reset, or deleted, their keys
are refused with 403 ACCOUNT_UNUSABLE. Changes a key makes are recorded in the audit log under its
owner, marked with the key's name.
Super Users see every key, with an Acts as column naming its owner. Everyone else sees and manages only their own keys.
Expiry. Give a key an Expires date and it works through the end of that day, in the
restaurant's time zone; after that it is refused with 401 like an unknown key. Leave it empty for
a key that never expires.
Switching a key off. The switch in the Status column stops a key without revoking it: it is
refused with 401 until you switch it back on.
Editing. Edit changes a key's name, permissions, expiry and switch. Its token stays the same, so the integration keeps working with its new permissions.
Revoking. Press Revoke on the key's row. Its very next request is refused with 401, and
this cannot be undone. A revoked key stays in the list so you can see what existed. The list also
shows when each key was last used.
The token from before 6.1. If your site had an API token before updating, it keeps working for exactly what it could do in 6.0 (reading the menu and placing orders), so nothing breaks on update. It does not gain any of the new permissions: to read orders, customers or reports, create a key that holds them. The keys panel says so. Give each integration its own key, then press Revoke on that notice to retire the old token.
Authentication
Every request carries a key:
Authorization: Bearer <your-api-key>
X-Api-Token: <your-api-key> is accepted as an alternative for clients that cannot set an
Authorization header.
Writes made with a key are recorded in the audit log as [API: <key name>], so you can tell which
integration changed what.
Rate limits
Each key may make 300 reads and 60 changes per minute by default. You can change both on
the REST API screen (0 turns a limit off). Every response tells the client where it stands:
RateLimit-Limit: 300
RateLimit-Remaining: 297
RateLimit-Reset: 41
Over the limit, the answer is 429 RATE_LIMITED with a Retry-After header, and nothing ran.
Two more limits protect the site regardless of the key: placing orders is limited to 60 per address per 5 minutes, booking tables to 30 per address per 5 minutes, and an address that sends 20 wrong keys in 5 minutes is refused for 5 minutes, on both lanes, whatever key it sends next. See Worked example & advice.
API health

With the API switched on, Configuration → REST API → API health checks that clients can actually use it:
| Check | What it means |
|---|---|
| Authorization header | Whether your web server passes Authorization: Bearer through to Joomla |
| API key | Whether at least one key (or the pre-6.1 token) is active |
| REST routes | Whether the Web Services - Next Restaurant plugin is enabled; without it /api/index.php/v1/nextrestaurant/… answers 404 |
| HTTPS | Whether the site is served over HTTPS, so keys aren't sent in clear |
The first three stop every client, so they are also raised as a banner across the whole admin, for users who can manage settings, until they are fixed. Re-check runs the header test again immediately; it also re-runs by itself once a day.
When the web server drops the Authorization header
Some servers remove the Authorization header before PHP ever sees it. Every bearer request then
fails with 401 Invalid API token, exactly as if the key were wrong. It is a server setting, not
something the extension can work around:
| Server | Drops the header? |
|---|---|
| nginx + PHP-FPM | No |
| Apache + mod_php | No |
| Apache + PHP-FPM or mod_fcgid (FastCGI) | Yes, unless configured |
The API health panel shows the fix for your server. On Apache, any one of these works:
-
Rename
htaccess.txtto.htaccessin the site root. Joomla's own file already contains the fix (and you likely want it for SEF URLs anyway). -
Or add this line to the virtual host or
.htaccess(Apache 2.4.13 or later):CGIPassAuth On -
Or, on mod_fcgid, add
FcgidPassHeader Authorizationto the virtual host.
On nginx, if a custom configuration strips it, add to the PHP location block:
fastcgi_param HTTP_AUTHORIZATION $http_authorization;
Until the server is fixed, clients can send the key as X-Api-Token: <your-api-key> instead —
custom headers are never stripped.
Next
- Conventions & errors — envelopes, paging, times and every error code
- Endpoint details — every endpoint with examples
- Worked example & advice — safe retries, concurrency and limits
- Troubleshooting — when a request is refused