Build on Dineator
The complete API reference and JavaScript SDK for integrating Dineator into your apps — from ordering flows and menu management to themes and analytics.
Quick Start
Install the SDK, authenticate, and make your first API call in minutes.
Install
npm install @dineator/sdk
Authenticate
JWT via /api/auth/login
Query
Call any resource endpoint
npm install @dineator/sdkimport { Dineator } from '@dineator/sdk';
const client = new Dineator({
apiUrl: 'https://api.dineator.com',
// Pass your JWT token after login
token: 'your-jwt-token',
});Authentication
Dineator uses JWT Bearer tokens. Login returns an accessToken (short-lived) and a refreshToken. Pass Authorization: Bearer <token> on all protected requests.
| Method | Path |
|---|---|
| POST | /api/auth/login |
| POST | /api/auth/register |
| POST | /api/auth/refresh |
| POST | /api/auth/logout |
| GET | /api/auth/me |
| POST | /api/auth/forgot-password |
| POST | /api/auth/reset-password |
| POST | /api/auth/magic-link |
| POST | /api/auth/verify-magic-link |
// POST /api/auth/login
const { data } = await client.auth.login({
email: 'admin@myrestaurant.com',
password: 'SecurePassword123!',
});
const token = data.accessToken;
// Store token — refresh at /api/auth/refresh using data.refreshTokenSDK Installation
The @dineator/sdk is a zero-dependency JavaScript/TypeScript client. It works in Node.js, Deno, browsers, and React Native.
npm install @dineator/sdkyarn add @dineator/sdk<script src='https://unpkg.com/@dineator/sdk@1.0.7/dist/dineator.min.js'></script>SDK Full Reference
The complete SDK source. Copy this into your project if you prefer vendoring, or install via npm.
// Complete SDK source — also available at:
// https://github.com/dineator/js-sdk
class DineatorError extends Error {
constructor(public status: number, public code: string, message: string) {
super(message);
this.name = 'DineatorError';
}
}
class Dineator {
private baseUrl: string;
private token: string | null;
constructor(opts: { apiUrl: string; token?: string }) {
this.baseUrl = opts.apiUrl;
this.token = opts.token ?? null;
}
setToken(token: string) { this.token = token; }
private async request<T>(
method: string, path: string, body?: unknown
): Promise<T> {
const res = await fetch(`${this.baseUrl}${path}`, {
method,
headers: {
'Content-Type': 'application/json',
...(this.token ? { Authorization: `Bearer ${this.token}` } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
const json = await res.json();
if (!res.ok) throw new DineatorError(
res.status, json.code ?? 'API_ERROR', json.message ?? 'Unknown error'
);
return json.data as T;
}
auth = {
login: (body: { email: string; password: string }) =>
this.request('POST', '/api/auth/login', body),
refresh: (body: { refreshToken: string }) =>
this.request('POST', '/api/auth/refresh', body),
me: () => this.request('GET', '/api/auth/me'),
logout: () => this.request('POST', '/api/auth/logout'),
forgotPassword: (body: { email: string }) =>
this.request('POST', '/api/auth/forgot-password', body),
};
menu = {
getPublic: (slug: string) =>
this.request('GET', `/api/menu/public/${slug}`),
listCategories: () => this.request('GET', '/api/menu/categories'),
createCategory: (body: object) =>
this.request('POST', '/api/menu/categories', body),
updateCategory: (id: string, body: object) =>
this.request('PUT', `/api/menu/categories/${id}`, body),
deleteCategory: (id: string) =>
this.request('DELETE', `/api/menu/categories/${id}`),
listItems: () => this.request('GET', '/api/menu/items'),
createItem: (body: object) =>
this.request('POST', '/api/menu/items', body),
updateItem: (id: string, body: object) =>
this.request('PUT', `/api/menu/items/${id}`, body),
deleteItem: (id: string) =>
this.request('DELETE', `/api/menu/items/${id}`),
};
orders = {
placePublic: (slug: string, body: object) =>
this.request('POST', `/api/orders/public/${slug}`, body),
placeAdmin: (body: object) =>
this.request('POST', '/api/orders/admin', body),
list: () => this.request('GET', '/api/orders'),
listKitchen: () => this.request('GET', '/api/orders/kitchen'),
updateStatus: (id: string, body: object) =>
this.request('PATCH', `/api/orders/${id}/status`, body),
setKitchenItemReadiness: (orderId: string, itemId: string, ready: boolean) =>
this.request('PATCH', `/api/orders/${orderId}/items/${itemId}/readiness`, { ready }),
trackPublic: (orderId: string, secret: string) =>
this.request('GET', `/api/orders/public/status/${orderId}?secret=${secret}`),
appendItems: (orderId: string, body: object) =>
this.request('PATCH', `/api/orders/${orderId}/append`, body),
splitBill: (id: string, body: object) =>
this.request('POST', `/api/orders/${id}/split`, body),
};
reservations = {
create: (slug: string, body: object) =>
this.request('POST', `/api/reservations/public/${slug}`, body),
checkAvailability: (slug: string, params: object) => {
const q = new URLSearchParams(params as Record<string,string>).toString();
return this.request('GET', `/api/reservations/public/${slug}/availability?${q}`);
},
list: () => this.request('GET', '/api/reservations'),
updateStatus: (id: string, body: object) =>
this.request('PATCH', `/api/reservations/${id}/status`, body),
};
tables = {
list: () => this.request('GET', '/api/tables'),
create: (body: object) => this.request('POST', '/api/tables', body),
update: (id: string, body: object) =>
this.request('PUT', `/api/tables/${id}`, body),
delete: (id: string) => this.request('DELETE', `/api/tables/${id}`),
};
analytics = {
getDashboard: (params: { range: string }) => {
const q = new URLSearchParams(params).toString();
return this.request('GET', `/api/analytics/dashboard?${q}`);
},
export: (params: object) => {
const q = new URLSearchParams(params as Record<string,string>).toString();
return this.request('GET', `/api/analytics/export?${q}`);
},
refreshSnapshot: () =>
this.request('POST', '/api/analytics/snapshot'),
};
theme = {
get: (restaurantId: string) =>
this.request('GET', `/api/restaurants/${restaurantId}/theme`),
update: (restaurantId: string, body: object) =>
this.request('PUT', `/api/restaurants/${restaurantId}/theme`, body),
reset: (restaurantId: string) =>
this.request('DELETE', `/api/restaurants/${restaurantId}/theme`),
};
promotions = {
getActiveOffers: (restaurantId: string) =>
this.request('GET', `/api/promotions/public/${restaurantId}/offers`),
validateCoupon: (restaurantId: string, body: object) =>
this.request('POST', `/api/promotions/public/${restaurantId}/validate-coupon`, body),
listCoupons: () => this.request('GET', '/api/promotions/coupons'),
createCoupon: (body: object) =>
this.request('POST', '/api/promotions/coupons', body),
updateCoupon: (id: string, body: object) =>
this.request('PUT', `/api/promotions/coupons/${id}`, body),
deleteCoupon: (id: string) =>
this.request('DELETE', `/api/promotions/coupons/${id}`),
listOffers: () => this.request('GET', '/api/promotions/offers'),
createOffer: (body: object) =>
this.request('POST', '/api/promotions/offers', body),
};
qr = {
list: () => this.request('GET', '/api/qr'),
generate: (body: object) => this.request('POST', '/api/qr/generate', body),
delete: (id: string) => this.request('DELETE', `/api/qr/${id}`),
};
}Orders API
Place and manage orders. Public routes are called from the customer storefront; admin routes are for staff and kitchen displays.
| Method | Path |
|---|---|
| POST | /api/orders/public/:slug |
| GET | /api/orders/public/status/:orderId?secret= |
| PATCH | /api/orders/:orderId/append |
| GET | /api/orders |
| GET | /api/orders/kitchen |
| PATCH | /api/orders/:orderId/items/:itemId/readiness |
| POST | /api/orders/admin |
| PATCH | /api/orders/:id/status |
| PATCH | /api/orders/:id/items |
| POST | /api/orders/:id/split |
// Place a public order (no auth — called from customer storefront)
const order = await client.orders.placePublic('restaurant-slug', {
tableId: 'table-uuid', // optional for DINE_IN
orderType: 'DINE_IN', // 'DINE_IN' | 'TAKEAWAY' | 'DELIVERY'
customerName: 'Jane Doe',
items: [
{ menuItemId: 'item-uuid', quantity: 2, notes: 'No onions' },
],
});
// Staff: place admin order (bypasses business-hours check)
const adminOrder = await client.orders.placeAdmin({
tableId: 'table-uuid',
orderType: 'DINE_IN',
items: [{ menuItemId: 'item-uuid', quantity: 1 }],
});
// Update order status
await client.orders.updateStatus('order-uuid', {
status: 'PREPARING',
});
// List orders (with kitchen view)
const kitchenOrders = await client.orders.listKitchen();PENDING → CONFIRMED → PREPARING → READY → DELIVERED → COMPLETED. Use CANCELLED to cancel.Reservations API
Accept and manage table reservations. Public endpoints power the customer-facing booking widget.
| Method | Path |
|---|---|
| POST | /api/reservations/public/:slug |
| GET | /api/reservations/public/:slug/availability |
| GET | /api/reservations/public/:slug/portal |
| GET | /api/reservations |
| PATCH | /api/reservations/:id/status |
// Create a public reservation (no auth)
const reservation = await client.reservations.create('restaurant-slug', {
guestName: 'John Doe',
guestPhone: '+1234567890',
guestEmail: 'john@example.com',
partySize: 4,
reservationDate: '2026-06-15',
reservationTime: '19:30',
notes: 'Window seat preferred',
});
// Check availability (no auth)
const slots = await client.reservations.checkAvailability(
'restaurant-slug',
{ date: '2026-06-15', partySize: 4 }
);
// Admin: update reservation status
await client.reservations.updateStatus('reservation-uuid', {
status: 'CONFIRMED', // 'CONFIRMED' | 'CANCELLED' | 'COMPLETED'
});Tables & Floors API
Manage the physical layout of your restaurant — floors and tables. All routes require authentication.
| Method | Path |
|---|---|
| GET | /api/tables |
| POST | /api/tables |
| PUT | /api/tables/:id |
| DELETE | /api/tables/:id |
| GET | /api/floors |
| POST | /api/floors |
| PATCH | /api/floors/:id |
| DELETE | /api/floors/:id |
| GET | /api/qr |
| POST | /api/qr/generate |
| DELETE | /api/qr/:id |
Analytics API
Access dashboard metrics, snapshots, and exportable reports. Requires MANAGER role or above.
| Method | Path |
|---|---|
| GET | /api/analytics/dashboard |
| POST | /api/analytics/snapshot |
| GET | /api/analytics/export |
// Get dashboard analytics (Manager+)
const dashboard = await client.analytics.getDashboard({
range: '7d', // '1d' | '7d' | '30d' | '90d'
});
// Export as CSV (Manager+)
const csvBlob = await client.analytics.export({
range: '30d',
format: 'csv',
});range values: 1d, 7d, 30d, 90dThemes API
Query and update restaurant themes. Themes are JSON token objects applied via CSS custom properties. See Theme Docs for the full token schema.
| Method | Path |
|---|---|
| GET | /api/restaurants/:id/theme |
| PUT | /api/restaurants/:id/theme |
| DELETE | /api/restaurants/:id/theme |
// Get theme for a restaurant (public)
const theme = await client.theme.get('restaurant-uuid');
// Update theme (Admin+)
await client.theme.update('restaurant-uuid', {
tokens: {
colors: {
primary: '#1d4ed8',
accent: '#f97316',
},
layout: {
headerStyle: 'frosted',
borderRadius: 'lg',
},
},
});
// Reset to the default colours and fonts (Admin+)
await client.theme.reset('restaurant-uuid');Promotions API
Manage discount coupons and limited-time offers. Public routes power the checkout coupon widget on the storefront.
| Method | Path |
|---|---|
| GET | /api/promotions/public/:restaurantId/offers |
| POST | /api/promotions/public/:restaurantId/validate-coupon |
| GET | /api/promotions/coupons |
| POST | /api/promotions/coupons |
| PUT | /api/promotions/coupons/:id |
| DELETE | /api/promotions/coupons/:id |
| GET | /api/promotions/offers |
| POST | /api/promotions/offers |
| PUT | /api/promotions/offers/:id |
| DELETE | /api/promotions/offers/:id |
// Get active offers (public)
const offers = await client.promotions.getActiveOffers('restaurant-uuid');
// Validate a coupon at checkout (public)
const coupon = await client.promotions.validateCoupon(
'restaurant-uuid',
{ code: 'SAVE10', orderTotal: 45.00 }
);
// Admin: Create a coupon
await client.promotions.createCoupon({
code: 'SUMMER20',
discountType: 'PERCENTAGE',
discountValue: 20,
minOrderAmount: 30,
maxUsages: 100,
expiresAt: '2026-08-31T23:59:59Z',
});Error Handling
All API responses follow a standard envelope. On error, the SDK throws a DineatorError.
| Status | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | Request body failed schema validation |
| 401 | UNAUTHORIZED | Missing or expired JWT token |
| 403 | FORBIDDEN | Valid token but insufficient role/permission |
| 404 | NOT_FOUND | Resource does not exist |
| 409 | CONFLICT | Unique constraint violation (e.g. duplicate slug) |
| 422 | UNPROCESSABLE | Business rule violation (e.g. restaurant closed) |
| 429 | RATE_LIMITED | Too many requests — back off and retry |
| 500 | INTERNAL_ERROR | Server-side error — contact support |
// The SDK throws DineatorError on non-2xx responses
import { DineatorError } from '@dineator/sdk';
try {
await client.menu.createItem({ /* ... */ });
} catch (err) {
if (err instanceof DineatorError) {
console.log(err.status); // HTTP status code, e.g. 422
console.log(err.code); // API error code, e.g. "VALIDATION_ERROR"
console.log(err.message); // Human-readable message
}
}Rate Limits
Rate limits protect the API from abuse. Limits are applied per IP on public endpoints and per user on authenticated endpoints.
| Route Group | Limit | Window |
|---|---|---|
| POST /api/auth/login | 10 requests | 15 minutes |
| POST /api/auth/register | 5 requests | 1 hour |
| POST /api/auth/magic-link | 5 requests | 15 minutes |
| POST /api/auth/forgot-password | 5 requests | 15 minutes |
| All other /api/* routes | 200 requests | 15 minutes |
429 RATE_LIMITED. Implement exponential backoff — wait at least 60 seconds before retrying.