The BEEPEC Orders API is a REST API that lets your own software (a booking system, a CRM, a photography business platform or an in-house tool) work with BEEPEC directly, without anyone placing orders by hand:
- Place orders: one request per property, with the address, the services you need, a link to your files and notes for the editor.
- Track them: check an order’s status at any time, or sync every order that changed since your last check.
- Collect deliveries: pick up the delivery link as soon as the edited files are ready.
Orders placed through the API are the same as orders placed in the BEEPEC client portal. They appear under My Orders, your team receives the usual emails, and they are billed on your account’s normal terms.
1. Before you start #
- Have a BEEPEC client account. If you don’t have one yet, create an account first.
- Request an API key. Email contact@beepec.com from your account’s email address and ask for API access. We issue one key per integration.
- Store the key securely. The key looks like
bpk_live_…and is shown to you only once. We keep only a fingerprint of it, so we can’t show it again. If it’s lost or exposed, ask us to revoke it and issue a new one.
Keep the key on your server. Anyone holding the key can place orders on your account. Store it as a secret or environment variable in your backend. Never put it in web-page code, a browser extension, a mobile app or a public code repository.
2. Base URL and authentication #
All endpoints live under this base URL:
https://drckjnvybgqeaegvljlf.supabase.co/functions/v1/client-api/v1
Send your key on every request as a bearer token:
Authorization: Bearer bpk_live_your_key_here
Check your setup by requesting your account details:
curl https://drckjnvybgqeaegvljlf.supabase.co/functions/v1/client-api/v1/account \
-H "Authorization: Bearer $BEEPEC_API_KEY"
{
"name": "Your Company",
"currency": "USD",
"billing_cadence": "weekly",
"can_order": true,
"credit": { "status": "ok", "outstanding": 0, "limit": 1000 }
}
can_order is false while unpaid invoices have reached your account’s credit limit. Pay them in the portal and ordering resumes.
Conventions #
- HTTPS only. Requests and responses are JSON in UTF-8.
- Times are ISO-8601 in UTC, for example
2026-10-18T02:04:28Z. - The API is versioned in the path (
/v1). Within version 1 we may add new fields to responses, but we never remove or rename existing ones, so ignore fields you don’t use.
3. Endpoints #
| Method | Path | What it does |
|---|---|---|
| GET | /v1/account |
Your account name, currency, billing terms and credit status |
| GET | /v1/services |
The services you can order, with your price per unit and usual turnaround |
| POST | /v1/orders |
Place an order |
| GET | /v1/orders/{order_number} |
One order, with its status, delivery and invoice |
| GET | /v1/orders |
Your orders, most recent first, with filters for syncing |
4. Services #
service code |
What it covers | Quantity counts |
|---|---|---|
photo_editing |
Single/HDR editing, pano stitching and editing | photos |
photo_retouching |
Object removal, room clean-up, season conversion, lawn replacement | photos |
floor_plan |
2D black/white, 2D full color, 2D texture, 3D full color | plans |
virtual_staging |
Virtual staging, renovation | rooms |
video_editing |
Custom video, virtual timelapse, reels | videos |
virtual_twilight |
Twilight conversion, warm sky, lit windows | exteriors |
For retouching, our QC team reviews the photos and sets the exact level (basic to extreme) before invoicing, so you don’t need to choose one. GET /v1/services returns this list with your account’s starting price per unit (unit_price_usd) and the usual turnaround in hours (turnaround_hours).
5. Placing an order #
POST /v1/orders
curl -X POST https://drckjnvybgqeaegvljlf.supabase.co/functions/v1/client-api/v1/orders \
-H "Authorization: Bearer $BEEPEC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"external_ref": "shoot-1042",
"name": "123 Example Street, Springfield",
"services": [
{ "service": "photo_editing", "quantity": 25 },
{ "service": "virtual_twilight", "quantity": 1 }
],
"input_links": [
{ "url": "https://www.dropbox.com/scl/fo/example?dl=0", "label": "RAW files" }
],
"instructions": "Please remove the realtor sign from the front lawn."
}'
| Field | Required | Rules |
|---|---|---|
name |
yes | The property address or job name, up to 200 characters. |
services |
yes | 1–10 entries, each service at most once. quantity is a whole number from 1 to 1000. |
input_links |
yes | 1–10 links to your files. Each url must start with https:// (Dropbox, Google Drive, WeTransfer and other file-sharing links work). Make sure anyone with the link can view the files. label is optional, up to 100 characters. |
instructions |
no | Plain text for the editor, up to 5,000 characters. Line breaks are kept; HTML is shown as text. |
external_ref |
recommended | Your own ID for this job (for example your shoot or booking ID), up to 100 printable characters. See “Safe retries” below. |
Response: 201 Created
{
"order": {
"order_number": "JOB-12345",
"external_ref": "shoot-1042",
"name": "123 Example Street, Springfield",
"status": "submitted",
"services": [
{ "service": "photo_editing", "name": "Single/HDR", "quantity": 25 },
{ "service": "virtual_twilight", "name": "Virtual Twilight", "quantity": 1 }
],
"created_at": "2026-10-18T02:04:28Z",
"deadline": "2026-10-18T14:04:28Z",
"delivered_at": null,
"delivery": null,
"invoice": null,
"portal_url": "https://portal.beepec.com/app/all-jobs?job=JOB-12345"
}
}
Save order_number. It’s how you look the order up later, and it’s the number to quote when you contact us.
Safe retries #
Always send external_ref. If a request times out or you’re not sure it arrived, send exactly the same request again:
- If we already have the order, you get
200 OKwith the existing order. Nothing is created twice. - If you reuse an
external_refwith different details, you get409 external_ref_conflict. Use a new reference for a new job.
Without external_ref, an identical order sent again within one minute is refused with 409 duplicate_order, to protect you from double submissions.
6. Tracking an order #
GET /v1/orders/{order_number} returns { "order": … } in the same shape as above.
status |
Meaning |
|---|---|
submitted |
Received, waiting to start |
processing |
Being edited or checked by our QC team |
revision_requested |
A revision is in progress |
on_hold |
Paused; we’ll contact you |
completed |
Delivered |
cancelled |
Cancelled |
When the order is delivered, delivery is filled in:
"delivery": {
"available": true,
"payment_required": false,
"links": [{ "label": "Edited photos", "url": "https://www.dropbox.com/…" }]
}
- If the files were uploaded to the portal instead of shared as a link, download them from
portal_url. - If your account pays before downloading,
availableisfalseandlinksis empty until the invoice is paid. invoiceshows the invoicenumber,status(pending,paidoroverdue) andamount_usdonce an invoice exists.
7. Listing and syncing orders #
GET /v1/orders returns your orders, most recent first, including orders placed in the portal.
| Query parameter | Example | Meaning |
|---|---|---|
status |
completed |
Only orders with this status |
external_ref |
shoot-1042 |
Find an order by your own ID |
updated_since |
2026-10-18T00:00:00Z |
Only orders that changed at or after this time |
limit |
100 |
Page size, 1–100 (default 20) |
offset |
100 |
Skip this many orders, for paging |
{ "orders": [ … ], "page": { "limit": 20, "offset": 0, "has_more": true } }
Recommended sync pattern. The API doesn’t push notifications, so check for changes on a schedule:
- Every 15–30 minutes, call
GET /v1/orders?updated_since=<time of your last successful sync>&limit=100. - Page with
offsetwhilehas_moreistrue. - For each order with
status: "completed", savedelivery.linksagainst your record (match it byexternal_refororder_number). - Store the time at which this sync started, and use it as
updated_sincenext time.
We also email delivery links to your account as usual, so your team is informed even if you don’t sync.
8. Errors #
Every error has the same shape:
{
"error": {
"code": "validation_failed",
"message": "services[0].service: unknown_service",
"details": { "services[0].service": "unknown_service" }
}
}
| HTTP | code |
What to do |
|---|---|---|
| 400 | invalid_json |
The body isn’t valid JSON. |
| 401 | missing_api_key, invalid_api_key |
Check the Authorization header and your key. |
| 402 | credit_limit_reached |
Unpaid invoices have reached your account’s limit. Pay them in the portal, then retry. |
| 403 | account_inactive |
Contact us. |
| 404 | order_not_found, not_found |
Check the order number or the path. |
| 405 | method_not_allowed |
Wrong HTTP method for this path. |
| 409 | duplicate_order, external_ref_conflict |
See “Safe retries”. |
| 422 | validation_failed |
Fix the fields listed in details. Don’t retry the same request unchanged. |
| 429 | order_limit_reached |
Your key’s daily order limit (50 by default) was reached. Contact us if you need more. |
| 500, 503 | internal_error, service_unavailable |
Temporary. Wait a little and retry with the same external_ref. |
Retry only network errors, timeouts and 5xx responses, with an increasing pause between attempts (for example 2, 4, then 8 seconds), and always with the same external_ref. Don’t retry 4xx responses without fixing the cause first.
9. Code examples #
JavaScript (Node.js 18 or later, server-side) #
// Keep the key in an environment variable on your server, never in browser code.
const BASE = "https://drckjnvybgqeaegvljlf.supabase.co/functions/v1/client-api/v1";
export async function placeBeepecOrder(job) {
const res = await fetch(`${BASE}/orders`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BEEPEC_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
external_ref: job.id, // your own ID: makes retries safe
name: job.address,
services: job.services, // e.g. [{ service: "photo_editing", quantity: 25 }]
input_links: [{ url: job.filesUrl, label: "RAW files" }],
instructions: job.notes ?? "",
}),
});
const body = await res.json();
if (!res.ok) {
// body.error.code says why. Timeouts and 5xx are safe to retry with the same external_ref.
throw new Error(`BEEPEC ${res.status} ${body.error?.code}: ${body.error?.message}`);
}
return body.order.order_number; // e.g. "JOB-12345"
}
Python (requests) #
import os
import requests
BASE = "https://drckjnvybgqeaegvljlf.supabase.co/functions/v1/client-api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['BEEPEC_API_KEY']}"}
def orders_changed_since(since_iso):
"""Every order that changed since the last sync, across all pages."""
orders, offset = [], 0
while True:
r = requests.get(f"{BASE}/orders", headers=HEADERS, timeout=30,
params={"updated_since": since_iso, "limit": 100, "offset": offset})
r.raise_for_status()
page = r.json()
orders.extend(page["orders"])
if not page["page"]["has_more"]:
return orders
offset += 100
for order in orders_changed_since("2026-10-18T00:00:00Z"):
if order["status"] == "completed" and order["delivery"] and order["delivery"]["available"]:
print(order["order_number"], order["external_ref"], [l["url"] for l in order["delivery"]["links"]])
Low-code platforms #
Automation tools and app builders (for example Zapier, Make, n8n or an app builder’s backend functions) can use the API through their HTTP request step. Put the key in the platform’s secret or credential store, call the API from a server-side step, and never from a page that runs in the browser.
10. Security checklist #
- Keep the API key server-side, in a secret store or environment variable.
- Use one key per integration, so a key can be revoked without affecting anything else.
- Never log the full key. Log the
order_numberanderror.codeinstead. - If a key may have been exposed, email us straight away and we’ll revoke it and issue a new one.
Help #
Email contact@beepec.com with your order_number, or the error.code and message you received, and we’ll help you.