Resources
Help & articles
Guides, how-tos and news for your catering business.
Developer
Puree's JSON API lets you read your account's data and, where the owner allows it, keep it up to date from your own tools.
Puree has a JSON API that lets you pull your own account’s data into your own scripts, spreadsheets and applications, and, where the account owner allows it, make changes: to your menu and packages, your customers, tasks and enquiries, your delivery run sheet and the rest of the areas listed below. The same access is what lets AI assistants work with your account. If all you want is to connect Claude, ChatGPT or Grok, you don’t need anything on this page: add the Puree connector to your AI app and click Sign in to Puree (see Connecting with an AI Agent). This page explains what the API offers and how to get started. The full, endpoint-by-endpoint reference lives in the interactive API docs.
Professional plan only
API and AI access, including this API and connecting AI apps, is part of the Professional plan. It’s switched on account by account, and the endpoints may still change. If you’re on Professional and don’t see API and AI access in Settings, email us at email@puree.app and we’ll switch it on.
The API is scoped to a single account: a token only ever sees the data belonging to the account it was created in. It’s designed for keeping Puree in step with your other tools, such as syncing customers from your CRM or into a mailing list, exporting your menu to another site, feeding order data into a reporting dashboard, or sending tomorrow’s deliveries to a routing app. Where you allow it, it can also make changes.
Access is given area by area. Each area can be looked up (Read), and most can also be changed (Write).
external_id: it’s unique within the account, so a re-run finds the customer instead of creating a second one. Contacts and addresses are made inactive rather than deleted, and a contact who has unsubscribed from emails can’t be resubscribed through the API.external_supplier_id; send null to remove it). A bundle can’t have a supplier.HH:MM on the stop’s own day. Two stops can share a run and position, as on the run sheet page.{customer_name} are kept exactly as written and filled in when the email is sent. Attached files can be read but not uploaded, and your terms and conditions aren’t part of this area: they’re managed in Puree, where every change is versioned.Nothing is ever deleted outright. The only removals are taking a contact out of a newsletter group, taking a person off a shift, and unlinking a document from a quote, and each leaves the contact, the person or the document itself in place (a document stays in your library). Everything else is made inactive, completed or retired instead, so nothing your quotes depend on disappears.
Photos. Item and package writes can carry a photo you supply, sent as base64 in the JSON body. It replaces the current photo, and Puree never fetches one from a web address.
data: prefix, line breaks or URL-safe characters.503 with a Retry-After header: wait that long and send it again.sha256, the SHA-256 of the file in hex, with a photo or an uploaded document, and Puree checks it arrived intact. If it doesn’t match, nothing changes and the error says so.Upload links. For a larger file, or one an AI assistant would otherwise have to copy out as base64, ask for an upload link with POST /uploads and purpose document or photo.
PUT the file’s bytes straight to the link.GET /uploads/{upload_id} says it’s ready, use its upload_id once:
POST /documents), so no customer sees it;GET /orders/{order_id}/dockets/{type} returns one PDF, where type is chef, driver, equipment, beverage, event_brief, consolidated or quote.
422 and the code not_applicable, so you can tell “doesn’t apply” from an error.?mode=link to get a link to the PDF instead of the file, for a tool that can’t pass a file on, such as an AI assistant. Anyone with the link can open it, without signing in, for 15 minutes.
GET /run_sheets/{date}, with the same Dockets permission, and ?mode=link works there too.Allergens are food safety, so the API says where each one comes from, and it never says a dish is allergen-free.
allergens, each marked manual (set by a person) or ai (found by Puree’s AI detection, which you should describe as detected rather than confirmed). recipe_allergens lists allergens in the recipe’s ingredients that aren’t on the item yet, with the ingredients they come from.option, snapshot (recorded on the line when it was added) or item_live (the dish’s current allergens). dietary_tags are as the quote prints them. A bundle shows its allergens on its content lines, and says so with allergens_on: "contents". These are the same allergens Puree’s quotes, dockets and labels print.looks_like_allergen, so treat it as an allergen warning even though it’s filed as a dietary tag.Only the account owner decides who can use API and AI access, and what each person may do.
Two-factor authentication (2FA) is required to grant access, to accept the terms, to create a token and to connect an AI app. If someone turns 2FA off, their tokens and connected apps are paused, not deleted, and work again as soon as they turn it back on. See Two-Factor Authentication.
All endpoints live under a single versioned base URL:
https://puree.app/api/public/v1
Every request must carry a personal access token. Once API access is enabled for your account and the owner has given you access, you can create one from Settings → API and AI access. Choose a name, tick the permissions you need (only what the owner granted is available) and, if you like, an expiry date. The token is shown once, so copy it straight away. Tokens look like puree_at_ followed by 40 hex characters. Treat a token like a password — anyone with it can use the access it carries, so store it securely and revoke it from the same page if it’s ever exposed.
Tokens and connected apps are listed separately
AI apps connected with Sign in to Puree don’t appear among your access tokens. They’re listed under Connected apps on the same page, where you can disconnect them. A connected app gets only what the person ticked when connecting, within what the owner gave them.
Pass the token in an Authorization header using the Bearer scheme. Here’s a complete example that lists your customers:
curl https://puree.app/api/public/v1/customers \
-H "Authorization: Bearer puree_at_your_token_here"
A request with a missing or invalid token comes back as 401 Unauthorized. A token that tries something it wasn’t given permission for is refused with 403. A paused token (because its holder has turned 2FA off) also gets 403, with the code two_factor_required.
To check a token, or to find out which account it works in, call GET /account. It needs no particular permission and returns the account’s name, time zone, currency and country, and the person the token acts for.
The list endpoints are paginated and support a search parameter for fuzzy matching (for example, on a customer’s name or a contact’s email). Combine query parameters as normal:
curl "https://puree.app/api/public/v1/customers?search=acme&page=2" \
-H "Authorization: Bearer puree_at_your_token_here"
A filter that takes a list of names, such as an item’s master list, takes them comma-separated and in any case: items?item_category_group=beverages,equipment. A name that doesn’t exist is refused with 400, never answered with an unfiltered list.
The delivery run sheet lists need a day: deliveries?date=2027-03-10, or from and to for up to 31 days. They’re always in run sheet order.
Orders carry expires_at and expired, and can be filtered by expiry and by number. expired is true only for a quote whose offer has lapsed (a draft, an expired quote, or a pending quote that was never accepted, past its expiry day): an order that has become a booking (accepted, confirmed, live, or an online order being processed) is never expired, whatever its date. The expiry filters go by date alone, whatever the state, so add state=pending for quotes about to lapse: orders?state=pending&expires_after=2027-03-01&expires_before=2027-03-14 lists the open quotes that expire in those two weeks (whole days in your time zone, both ends included, so a quote expiring late on the 14th counts). orders?reference=1042 finds the quote the app shows as #1042 (#1042 works too); a number no quote has returns an empty list.
Each access token you create is limited to 60 requests per minute. An AI app connected with Sign in to Puree gets more, because answering a question takes many small lookups: 300 reads and 120 changes a minute. All the tokens and apps on one account share a ceiling of 600 requests a minute between them.
If you go over, requests return 429 Too Many Requests with a Retry-After header saying how many seconds are left before you can try again. Wait that long and retry. Docket and run sheet PDFs have tighter limits of their own (see Docket and Quote PDFs). If you’re building something that needs a higher limit, get in touch.
To change many items or packages at once, send up to 50 in one request with PATCH /items/bulk or PATCH /packages/bulk. Each is saved on its own, so one that fails doesn’t stop the rest, and the response says which were updated and why any weren’t: retry only the failed ones. Photos can’t be changed in bulk.
Write requests must send JSON, with Content-Type: application/json. Anything else is refused with 415 Unsupported Media Type before it’s read.
Errors come back as JSON with an errors array. Each entry has a machine-readable code, a short title, and sometimes a longer detail:
{
"errors": [
{ "code": "unauthorized", "title": "Missing or invalid access token" }
]
}
The ones you’re most likely to meet:
| Status | Code | Means |
|---|---|---|
| 400 | invalid_request |
A value was malformed: an unknown key, a wrong type, a bad filter, or a change to something that can only be read. Nothing was written. |
| 401 | unauthorized |
The token is missing or invalid. |
| 403 |
insufficient_scope, two_factor_required
|
The token doesn’t have this permission, or is paused. |
| 404 | not_found |
It isn’t on this account. |
| 409 | conflict |
It would duplicate something that must be unique, such as a name or a customer’s external_id. Nothing was written. |
| 415 | unsupported_media_type |
The body wasn’t sent as JSON. |
| 422 |
validation_failed, not_applicable
|
The values didn’t pass validation (each error names its field), or the docket you asked for doesn’t apply to that quote. |
| 429 | rate_limited |
Too many requests; wait and retry. |
Everything done through a token or a connected AI app is recorded on the Activity log tab of the API and AI access page, in plain English, for example “Changed the item Crispy pork belly: moved from position 24 to 16”. Each row shows who, which connection, what changed and whether it worked. You can search, filter by person, connection, type and dates, and click a row for the full details. The owner sees everyone’s activity; everyone else sees their own. Entries are kept for 13 months, and tokens and other secrets are never shown. See API and AI Access: Decide Who Can Connect AI to Your Puree Account for the full picture, and Connecting with an AI Agent if you’re connecting an AI assistant.
Every endpoint, parameter and response field is documented in the interactive API reference. It’s a reference rather than a console: try calls from your own tools, with a token you’ve created.
Questions, or want access enabled for your account? Email us at email@puree.app.