Filld Developer Documentation
SCN Portal Integration for Evergreen
Integrate your SCN platform with Evergreen Kosher Market's delivery operation — check live slot availability, book delivery windows, and create orders through one API. Book a window in one call when the order is placed, or point your existing order integration at us.
Introduction
SCN Portal Integration for Evergreen — the Partner Slots API, built and operated by Filld — gives SCN platforms programmatic access to Evergreen Kosher Market's real delivery capacity. Check which delivery windows are available for a customer's address and book one — a single call — when the order is placed, so every platform draws from the same capacity pool and customers are never promised a time the store cannot honor.
Filld operates the platform and issues your API keys; the API fronts Evergreen's stores and delivery operation.
All slot dates and windows are store-local wall times (America/New_York). orderBy and every other timestamp are ISO 8601 UTC instants.
A full sandbox is available: test keys run the entire API — real windows, real validation — with zero real-world impact. See Sandbox & Testing.
Base URL
Everything lives under one base URL:
https://api.filld.dev/api/v1Order endpoints (/orders, /orders/id/{orderNumber}, /orders/external/{externalOrderId}) and delivery endpoints (/delivery/availability, /delivery/reservations) all live under it.
Authentication
Authenticate every request with an API key sent in the x-api-key header. Keys are issued per organization by Filld and begin with evg_live_. Requests without a valid key receive 401 unauthorized. Test keys (evg_test_) work in the same header and run the full API against the sandbox — see Sandbox & Testing.
x-api-key: evg_live_XXXXXXXXXXXXXXXXKeep your key server-side
Your Evergreen key is the only credential you need. On the order endpoints, the X-Customer-Pin header is required when payment.method is "OnAccount" or when paying with a stored card.
Sandbox & Testing
API keys come in two modes, sent in the same x-api-key header: live keys begin with evg_live_; test keys begin with evg_test_ and are issued the same way by Filld, from the same organization. A test-key request runs the entire API identically — same endpoints, same validation, real zones, real windows, real order-by deadlines, and realistic slot_unavailable responses when a window is genuinely full — but with zero real-world impact:
- Reservations. Sandbox reservations never consume real capacity, and real traffic never blocks on them. They follow the same lifecycle — immediate bookings, opt-in holds, cancel, expire.
- Orders. Orders created with a test key are not actually placed or fulfilled — you receive a
TEST--prefixed order number and can exercise the full flow:201with an empty body, aLocation: /orders/id/TEST-48210937header, and the extra headerx-evergreen-sandbox: true. - Failure simulation. Include the string
simulate=pos_erroranywhere in the order'snotefield — the sandbox returns a400error in the standard problem+json format so you can exercise failure handling; any capacity taken by the call is released, as with a real failure. - Order status.
GET /orders/id/{orderNumber}for aTEST-id returns a stub order:{ id, externalOrderId, status: "OrderEntered", pickupDeliveryTime, items: [], sandbox: true }. - Tracking & editing. Tracking works in sandbox but stays at
sent_to_pos— there is no real fulfillment. Editing is live-only: sandbox orders are treated as sent immediately, soPATCH /orders/id/…returns409 order_not_editable. - Isolation. Test and live data are fully isolated: a test key sees only reservations and orders created by test keys — cross-mode lookups return
404— and vice versa.
Integrate and run CI against your test key, then switch the key — one environment variable — to go live.
curl -X POST "https://api.filld.dev/api/v1/orders" \
-H "x-api-key: evg_test_XXXXXXXXXXXXXXXX" \
-H "Content-Type: application/json" \
-d '{
"customerId": "18452",
"orderMethod": "Delivery",
"pickupDeliveryTime": "2026-07-07T10:00:00",
"externalOrderId": "cart_88213",
"items": [
{ "productCode": "412009", "quantity": 2, "unitPrice": 4.99 }
],
"deliveryAddress": {
"name": "Chaim Brachfeld",
"phoneNumber": "8455550142",
"address": {
"addressLine1": "12 Main St",
"city": "Monsey",
"state": "NY",
"zipCode": "10952"
}
}
}'HTTP/1.1 201 Created
Location: /orders/id/TEST-48210937
x-evergreen-sandbox: trueSandbox availability is real availability
Content Type
Send request bodies as JSON with the Content-Type: application/json header. All responses are returned as JSON.
Rate Limiting
Requests are limited to 120 per minute per organization by default. Requests over the limit receive 429 rate_limited. Back off and retry after a short delay. Contact Filld if your integration needs a higher limit.
Reserving a Slot
Reserving books the slot — one call, done. POST /delivery/reservations returns a reservation with status confirmed; the slot is consumed until the delivery date passes. Reserve at the moment the customer places the order (right before you send it to the POS), and you never need to touch the reservation again.
- Customer enters their address — check availability (
POST /delivery/availability). - Customer picks a window and places the order — reserve the slot (
POST /delivery/reservations), then send the order to the POS exactly as you do today. - Optional: attach your POS order number via
POST /reservations/{id}/confirm; callDELETEif your order failed.
Important
One call, no availability lookup
Already know the time your customer picked? Skip availability entirely: send the address, the time, and (optionally) your POS order number — one call, booked. Pass time instead of slotId; the reservation is booked into whichever offered window covers the requested time (Eastern; an offset-less time is taken as Eastern wall time).
curl -X POST "https://api.filld.dev/api/v1/delivery/reservations" \
-H "x-api-key: evg_live_XXXXXXXXXXXXXXXX" \
-H "Content-Type: application/json" \
-d '{
"time": "2026-07-09T10:30:00",
"posOrderRef": "15009999",
"address": {
"street": "12 Main St",
"city": "Monsey",
"state": "NY",
"zip": "10952"
}
}'{
"reservationId": "res_4b1e77c2a90f",
"status": "confirmed",
"slot": {
"slotId": "slot_UGsxNDQzfDIwMjYtMDctMDk",
"date": "2026-07-09",
"window": { "start": "09:00", "end": "12:00" },
"label": "Thu, Jul 9 · 9:00 AM – 12:00 PM"
},
"zone": { "id": "zone_mnsy01", "name": "Monsey" },
"expiresAt": null,
"customerRef": null,
"partnerOrderRef": null,
"posOrderRef": "15009999",
"createdAt": "2026-07-03T14:15:00.000Z",
"confirmedAt": "2026-07-03T14:15:00.000Z",
"cancelledAt": null
}The response tells you the actual delivery window the time landed in (slot.window / slot.label— here 10:30 resolved to the 9:00 AM–12:00 PM window) — display that window to the customer, not the raw time.
Optional: checkout-start holds
To grab capacity when checkout starts instead, pass ttlMinutes (5–120) on the reserve call. The reservation is created as held with expiresAt set; confirm it (POST /reservations/{id}/confirm) before it lapses, or the hold expires automatically and the capacity is released. Confirming after it lapses returns 409 reservation_expired — reserve again.
Reservation states
confirmed— booked. The default: reserving withoutttlMinutesreturnsconfirmedimmediately, withexpiresAt: null.held— opt-in hold mode only: capacity is taken, but the reservation must be confirmed beforeexpiresAt.cancelled— released via DELETE, e.g. when the customer cancels the order.expired— an opt-in hold that lapsed without confirmation. Cannot be confirmed — reserve again.
Sending Orders
The Orders API is contract-compatible with the POSwithLogic API you already use — set your integration's base URL to https://api.filld.dev/api/v1 (instead of https://api.poswithlogic.dev) and use your Evergreen x-api-key. Your existing /orders calls work unchanged: same paths, same request body, same responses, same error format. Migrating is exactly two changes:
- Base URL:
https://api.poswithlogic.dev→https://api.filld.dev/api/v1 x-api-key: your POS key → your Evergreen key
Nothing else changes — the order endpoints use the same RFC 7807 problem+json error format the POS API uses. POST /orders creates the order; the full field-by-field schema is documented under Send an Order.
Delivery scheduling
pickupDeliveryTimeis the customer's requested time (Eastern; an offset-less time is taken as Eastern wall time). It is booked into the delivery window that covers it (see Check Availability); the scheduled time stamped on the created order may be adjusted within that window as the store paces its day.
A requested time before or between windows books the next available window later that same day. A time past the day's last window (or a closed day) is still accepted (201) — flagged as slotOverride "unscheduled"; a past-deadline window flags "deadline". A full window rejects with 409 slot_full — check availability and pick another time (a capacity boost granted to your organization bypasses this). An address outside the delivery area is accepted for review: 201 with an x-evergreen-warning header and a JSON body ("outside_delivery_area") — the order is parked with the store; contact support to arrange fulfillment. An unparseable time rejects with 400 invalid_request.
Important
Important
Responses
A successful create returns 201 Created with an empty body and a Location: /orders/id/{orderNumber} header (the Location value is relative to the base URL). Additional response headers carry scheduling and reservation info:
x-evergreen-reservation-id— the reservation backing this order, confirmed automatically when the order is created.x-evergreen-delivery-time— the scheduled time on the created order.x-evergreen-store— the fulfilling store slug.x-evergreen-window— the customer-facing window,YYYY-MM-DD HH:MM-HH:MM.x-evergreen-sandbox—trueon sandbox responses.
If the order cannot be created, the response is an error in the format described in Error Handling, and any window capacity taken by the call is released — a reservation you referenced stays intact so you can retry.
Optional Evergreen extensions
Three extra body fields may be included alongside the standard schema: reservationId — attach a previously reserved slot to the order instead of booking a window on the fly (the reservation is confirmed automatically when the order is created); store (pickup orders only) — which store fulfills the pickup, e.g. "uptown"; and priority — integer 1 | 2 | 3 (1 = highest, omitted = normal) — the store sees it and can use it to sequence work. All are entirely optional; a standard payload works as-is.
Pickup orders
Pickup orders involve no slot. Send orderMethod: "Pickup" (or omit deliveryAddress) and optionally set store to choose the fulfilling store.
Order Tracking
Every order carries a normalized lifecycle status that Evergreen updates continuously as the order moves through the store:
accepted → sent_to_pos → picking → checked_out → label_created → out_for_delivery → delivered (plus cancelled)
accepted— Evergreen has the order; it has not yet been released to fulfillment. The only state in which an order can still be edited.sent_to_pos— in the store's fulfillment queue.picking— a shopper is picking the order.checked_out— picked and rung up.label_created— the delivery label has been made.out_for_delivery— a driver is en route; anetabecomes available.delivered— terminal.
Statuses update within about 5 minutes of store-side changes; delivery milestones within about 3 minutes. A timeline entry is appended on every transition, so you always have the full history.
Consuming tracking
Poll GET /api/v1/orders with since=<your last poll time> every minute or two — it returns only the orders whose tracking changed after that instant, which makes incremental updates cheap. Tracking a specific set of open orders instead? Send their ids to POST /api/v1/orders/batch and get exactly those rows. The per-order lookups (/orders/id/{orderNumber}, /orders/external/{externalOrderId}) include the same information as a tracking object.
Integration Flow
Three recipes are supported. All draw from the same capacity pool.
One-call booking (simplest)
Keep your checkout exactly as it is; book the window with a single call.
- Customer picks a delivery time in your checkout.
- When the order is placed — one
POST /reservationscall with the address, thetime, and (optionally)posOrderRef→ booked; send the order to the POS as usual. - Track the order — poll
GET /api/v1/orderswithsince=…(see Order Tracking).
Availability-first
Show customers our real windows and order-by deadlines, then book the one they pick.
- Customer enters their address — send
POST /availability. - Customer picks a window and places the order — send
POST /reservationswith theslotIdto book the slot, then send the order to the POS exactly as you do today. - Optional: attach your POS order number via
POST /reservations/{id}/confirm;DELETEthe reservation if your order failed. - Track the order — poll
GET /api/v1/orderswithsince=….
Drop-in migration (recommended)
Change the base URL and the API key — keep sending orders exactly as you do today.
- Set your integration's base URL to
https://api.filld.dev/api/v1and swapx-api-keyto your Evergreen key. - Keep sending
POST /orderswith your existing payloads — the delivery window is validated and booked in the same call. - Optionally adopt
POST /api/v1/delivery/availabilityand/reservationsfor a better slot-picking UX — hold a window at checkout start and pass it via the optionalreservationIdextension field on the order. - Track your orders — poll
GET /api/v1/orderswithsince=….
Endpoints
All endpoints live under the base URL. Every request requires the x-api-key header, and reservation endpoints operate only on reservations that belong to your organization. Prefer a scannable list? See the API Reference.
Check Availability
Auth: API key/api/v1/delivery/availabilityCheck whether an address is inside the delivery area and which delivery windows are currently open for it. Provide the full street address whenever you have it — street, city, state, and ZIP together resolve against precise polygon coverage, while a ZIP alone falls back to coarser ZIP-level coverage. Optional if you reserve by time — but recommended for showing customers accurate windows and order-by deadlines.
| Parameter | Type | Required | Description |
|---|---|---|---|
| address | object | Required | The delivery address to check. The same fields are also accepted flat at the top level of the body. |
| address.street | string | Recommended | Street address, including the house number. |
| address.city | string | Recommended | City name. |
| address.state | string | Recommended | Two-letter state code. |
| address.zip | string | Required | 5-digit ZIP code. Send the full address as well — ZIP-only checks use coarser coverage. |
addressobjectRequired- The delivery address to check. The same fields are also accepted flat at the top level of the body.
address.streetstringRecommended- Street address, including the house number.
address.citystringRecommended- City name.
address.statestringRecommended- Two-letter state code.
address.zipstringRequired- 5-digit ZIP code. Send the full address as well — ZIP-only checks use coarser coverage.
Testing tip
GET /api/v1/delivery/availability?street=&city=&state=&zip=. Use the POST form in production.curl -X POST "https://api.filld.dev/api/v1/delivery/availability" \
-H "x-api-key: evg_live_XXXXXXXXXXXXXXXX" \
-H "Content-Type: application/json" \
-d '{
"address": {
"street": "12 Main St",
"city": "Monsey",
"state": "NY",
"zip": "10952"
}
}'{
"covered": true,
"zone": { "id": "zone_mnsy01", "name": "Monsey" },
"slots": [
{
"slotId": "slot_UGsxNDQzfDIwMjYtMDctMDc",
"date": "2026-07-07",
"dayOfWeek": 2,
"window": { "start": "09:00", "end": "12:00" },
"label": "Tue, Jul 7 · 9:00 AM – 12:00 PM",
"orderBy": "2026-07-06T19:00:00.000Z",
"availability": "available"
},
{
"slotId": "slot_UGsxNDQzfDIwMjYtMDctMDg",
"date": "2026-07-08",
"dayOfWeek": 3,
"window": { "start": "14:00", "end": "17:00" },
"label": "Wed, Jul 8 · 2:00 PM – 5:00 PM",
"orderBy": "2026-07-08T04:00:00.000Z",
"availability": "limited"
}
]
}When the address is outside the delivery area, the response is covered: false with zone: null and an empty slots array.
slotIdis an opaque token. Pass it back verbatim when reserving; do not parse it or construct your own.availabilityis"available"or"limited"(only a few spots left).orderByis the instant after which the slot can no longer be reserved.
Response statuses
200Availability returned.400invalid_request— the address is missing or malformed.401unauthorized— missing or invalid API key.429rate_limited— too many requests.
Reserve a Slot
Auth: API key/api/v1/delivery/reservationsBooks a delivery slot. By default the reservation comes back confirmed — the slot is booked and nothing else is required. Provide slotId (from /availability) or time— the customer's requested delivery time. Pass ttlMinutes for an opt-in checkout-start hold instead.
| Parameter | Type | Required | Description |
|---|---|---|---|
| slotId | string | Conditional | The slot token returned by /availability, passed back verbatim. Provide slotId or time. |
| time | string | Conditional | ISO 8601 date-time, Eastern (offset-less = Eastern wall time) — the customer's requested delivery time. The reservation is booked into whichever offered window covers it. Provide slotId or time. |
| address | object | Required | The delivery address for the order. |
| posOrderRef | string | Optional | Your POS order number, attached at creation — no confirm call needed. |
| customerRef | string | Optional | Your identifier for the customer. Opaque to Evergreen; echoed back on the reservation. |
| partnerOrderRef | string | Optional | Your order or cart identifier. |
| ttlMinutes | number | Optional | Opt-in hold mode: creates the reservation as "held" with an expiry, from 5 to 120 minutes; confirm it before it lapses. Omit for the default — an immediately confirmed booking. |
slotIdstringConditional- The slot token returned by
/availability, passed back verbatim. ProvideslotIdortime. timestringConditional- ISO 8601 date-time, Eastern (offset-less = Eastern wall time) — the customer's requested delivery time. The reservation is booked into whichever offered window covers it. Provide
slotIdortime. addressobjectRequired- The delivery address for the order.
posOrderRefstringOptional- Your POS order number, attached at creation — no confirm call needed.
customerRefstringOptional- Your identifier for the customer. Opaque to Evergreen; echoed back on the reservation.
partnerOrderRefstringOptional- Your order or cart identifier.
ttlMinutesnumberOptional- Opt-in hold mode: creates the reservation as "held" with an expiry, from 5 to 120 minutes; confirm it before it lapses. Omit for the default — an immediately confirmed booking.
curl -X POST "https://api.filld.dev/api/v1/delivery/reservations" \
-H "x-api-key: evg_live_XXXXXXXXXXXXXXXX" \
-H "Content-Type: application/json" \
-d '{
"slotId": "slot_UGsxNDQzfDIwMjYtMDctMDc",
"address": {
"street": "12 Main St",
"city": "Monsey",
"state": "NY",
"zip": "10952"
},
"customerRef": "cust_123"
}'{
"reservationId": "res_9f2c7a1e4b8d",
"status": "confirmed",
"slot": {
"slotId": "slot_UGsxNDQzfDIwMjYtMDctMDc",
"date": "2026-07-07",
"window": { "start": "09:00", "end": "12:00" },
"label": "Tue, Jul 7 · 9:00 AM – 12:00 PM"
},
"zone": { "id": "zone_mnsy01", "name": "Monsey" },
"expiresAt": null,
"customerRef": "cust_123",
"partnerOrderRef": null,
"posOrderRef": null,
"createdAt": "2026-07-03T14:15:00.000Z",
"confirmedAt": "2026-07-03T14:15:00.000Z",
"cancelledAt": null
}This reservation object is the shape returned by every reservation endpoint. expiresAt is null for the default confirmed booking; in opt-in hold mode it is the instant the hold lapses if it is not confirmed. When reserving by time, slot tells you the delivery window the time landed in — display that window to the customer.
Response statuses
201Slot booked (or hold created, whenttlMinutesis passed).400invalid_slot_id— the slot token is malformed.invalid_request— the body is malformed, or neitherslotIdnor a parseabletimewas provided.409slot_unavailable— the window is full or no longer offered, or no delivery window covers the requestedtime; re-fetch availability.order_by_passed— the window'sorderBydeadline has passed.422address_not_covered— the address is outside the delivery area.zone_mismatch— the slot does not serve that address.401unauthorized— missing or invalid API key.429rate_limited— too many requests.
List Reservations
Auth: API key/api/v1/delivery/reservationsList your organization's reservations, most recent first. Returns up to 100 reservations.
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | Optional | Filter by status: one of held, confirmed, cancelled, expired. |
| date | string | Optional | Filter by delivery date, YYYY-MM-DD (store-local). |
statusstringOptional- Filter by status: one of
held,confirmed,cancelled,expired. datestringOptional- Filter by delivery date,
YYYY-MM-DD(store-local).
curl "https://api.filld.dev/api/v1/delivery/reservations?status=confirmed&date=2026-07-07" \
-H "x-api-key: evg_live_XXXXXXXXXXXXXXXX"{
"reservations": [
{
"reservationId": "res_9f2c7a1e4b8d",
"status": "confirmed",
"slot": {
"slotId": "slot_UGsxNDQzfDIwMjYtMDctMDc",
"date": "2026-07-07",
"window": { "start": "09:00", "end": "12:00" },
"label": "Tue, Jul 7 · 9:00 AM – 12:00 PM"
},
"zone": { "id": "zone_mnsy01", "name": "Monsey" },
"expiresAt": "2026-07-03T14:45:00.000Z",
"customerRef": "cust_123",
"partnerOrderRef": "cart_88213",
"posOrderRef": "SO-448291",
"createdAt": "2026-07-03T14:15:00.000Z",
"confirmedAt": "2026-07-03T14:22:31.000Z",
"cancelledAt": null
}
]
}Response statuses
200Reservations returned.401unauthorized— missing or invalid API key.429rate_limited— too many requests.
Get a Reservation
Auth: API key/api/v1/delivery/reservations/{id}Fetch a single reservation by its reservationId. A reservation that belongs to another organization returns 404, the same as one that does not exist.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Required | The reservationId returned when the reservation was created. |
idstringRequired- The
reservationIdreturned when the reservation was created.
curl "https://api.filld.dev/api/v1/delivery/reservations/res_9f2c7a1e4b8d" \
-H "x-api-key: evg_live_XXXXXXXXXXXXXXXX"{
"reservationId": "res_9f2c7a1e4b8d",
"status": "confirmed",
"slot": {
"slotId": "slot_UGsxNDQzfDIwMjYtMDctMDc",
"date": "2026-07-07",
"window": { "start": "09:00", "end": "12:00" },
"label": "Tue, Jul 7 · 9:00 AM – 12:00 PM"
},
"zone": { "id": "zone_mnsy01", "name": "Monsey" },
"expiresAt": null,
"customerRef": "cust_123",
"partnerOrderRef": null,
"posOrderRef": null,
"createdAt": "2026-07-03T14:15:00.000Z",
"confirmedAt": "2026-07-03T14:15:00.000Z",
"cancelledAt": null
}Response statuses
200Reservation returned.404not_found— no such reservation in your organization.401unauthorized— missing or invalid API key.
Confirm a Reservation
Auth: API key/api/v1/delivery/reservations/{id}/confirmAttach your POS order number to a reservation (optional). Include posOrderRef so the store can reconcile the reservation against your order. The call is required only to keep an opt-in hold alive — confirming a held reservation before expiresAt turns it into a booking. Idempotent: confirming an already-confirmed reservation updates the references and returns 200.
| Parameter | Type | Required | Description |
|---|---|---|---|
| posOrderRef | string | Optional | Your order number for the placed order. Strongly recommended. |
| partnerOrderRef | string | Optional | Your order or cart identifier. |
| customerRef | string | Optional | Your identifier for the customer. |
posOrderRefstringOptional- Your order number for the placed order. Strongly recommended.
partnerOrderRefstringOptional- Your order or cart identifier.
customerRefstringOptional- Your identifier for the customer.
curl -X POST "https://api.filld.dev/api/v1/delivery/reservations/res_9f2c7a1e4b8d/confirm" \
-H "x-api-key: evg_live_XXXXXXXXXXXXXXXX" \
-H "Content-Type: application/json" \
-d '{
"posOrderRef": "SO-448291",
"partnerOrderRef": "cart_88213"
}'{
"reservationId": "res_9f2c7a1e4b8d",
"status": "confirmed",
"slot": {
"slotId": "slot_UGsxNDQzfDIwMjYtMDctMDc",
"date": "2026-07-07",
"window": { "start": "09:00", "end": "12:00" },
"label": "Tue, Jul 7 · 9:00 AM – 12:00 PM"
},
"zone": { "id": "zone_mnsy01", "name": "Monsey" },
"expiresAt": "2026-07-03T14:45:00.000Z",
"customerRef": "cust_123",
"partnerOrderRef": "cart_88213",
"posOrderRef": "SO-448291",
"createdAt": "2026-07-03T14:15:00.000Z",
"confirmedAt": "2026-07-03T14:22:31.000Z",
"cancelledAt": null
}Response statuses
200References updated (and, in hold mode, the hold becomes a booking).409reservation_expired— (hold mode) the hold lapsed before confirmation; reserve a new slot.reservation_cancelled— the reservation was cancelled and cannot be confirmed.404not_found— no such reservation in your organization.401unauthorized— missing or invalid API key.
Cancel a Reservation
Auth: API key/api/v1/delivery/reservations/{id}Cancel a hold or a confirmed reservation and release its capacity — for example, when the customer cancels the order or switches to a different window. Cancelling an already-cancelled or expired reservation is a no-op that returns the current state.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Required | The reservationId to cancel. |
idstringRequired- The
reservationIdto cancel.
curl -X DELETE "https://api.filld.dev/api/v1/delivery/reservations/res_9f2c7a1e4b8d" \
-H "x-api-key: evg_live_XXXXXXXXXXXXXXXX"{
"reservationId": "res_9f2c7a1e4b8d",
"status": "cancelled",
"slot": {
"slotId": "slot_UGsxNDQzfDIwMjYtMDctMDc",
"date": "2026-07-07",
"window": { "start": "09:00", "end": "12:00" },
"label": "Tue, Jul 7 · 9:00 AM – 12:00 PM"
},
"zone": { "id": "zone_mnsy01", "name": "Monsey" },
"expiresAt": "2026-07-03T14:45:00.000Z",
"customerRef": "cust_123",
"partnerOrderRef": "cart_88213",
"posOrderRef": null,
"createdAt": "2026-07-03T14:15:00.000Z",
"confirmedAt": null,
"cancelledAt": "2026-07-03T14:31:02.000Z"
}Response statuses
200Reservation cancelled (or already inactive).404not_found— no such reservation in your organization.401unauthorized— missing or invalid API key.
Send an Order
Auth: API key/api/v1/ordersCreate an order. Returns 201 Created with a Location header carrying the order number; the delivery window is validated and booked in the same call.
| Header | Type | Required | Description |
|---|---|---|---|
| x-api-key | string | Required | Your API key. |
| X-Customer-Pin | string | Conditional | Required when payment.method is "OnAccount" or when paying with a stored card. |
| Content-Type | string | Required | application/json. |
x-api-keystringRequired- Your API key.
X-Customer-PinstringConditional- Required when
payment.methodis"OnAccount"or when paying with a stored card. Content-TypestringRequiredapplication/json.
| Parameter | Type | Required | Description |
|---|---|---|---|
| customerId | string (≤8 chars) | Conditional | An existing customer account id. Provide customerId or customer. |
| customer | object | Conditional | Used when no customerId is provided. All fields within it are optional. |
| customer.firstName | string | Optional | Customer first name. |
| customer.lastName | string | Optional | Customer last name. |
| customer.phone | string | Optional | Customer phone number. |
| customer.email | string | Optional | Customer email address. |
| customer.address | object | Optional | addressLine1 (≤30 chars), addressLine2, city, state, zipCode. |
| externalOrderId | string (≤20 chars) | Optional | Your own order reference; must be unique per order. Used by GET /orders/external/{externalOrderId} and returned in webhook calls. |
| status | string | Optional | Initial order status: "OrderEntered" or "Unpicked". |
| items | array | Required | Order lines. Must be non-empty. |
| items[].productCode | string (≤12 chars) | Required | The item barcode. |
| items[].quantity | number | Required | Units; for by-weight lines ("Lbs"), the weight in pounds. |
| items[].description | string (≤30 chars) | Optional | Line description. |
| items[].priceQty | number | Optional | A divisor that determines the actual unit price (unitPrice ÷ priceQty). |
| items[].unitOfMeasure | string | Optional | "Unit", "Case", or "Lbs". |
| items[].unitPrice | number | Optional | Price per unit, in dollars. |
| items[].totalPrice | number | Optional | Line total, in dollars. |
| items[].note | string | Optional | Line note, visible to pickers and kitchen staff. |
| deliveryAddress | object | Delivery | The recipient of the order. |
| deliveryAddress.name | string | Optional | Recipient name. |
| deliveryAddress.phoneNumber | string | Optional | Recipient phone number. |
| deliveryAddress.address | object | Delivery | addressLine1 (≤30 chars), addressLine2, city (required for delivery), state, zipCode. |
| orderMethod | string | Optional | "Pickup" or "Delivery". Defaults to Delivery when deliveryAddress is present. |
| pickupDeliveryTime | string | Conditional | ISO 8601 date-time, Eastern time — the customer's requested time (see Delivery scheduling). Required for Delivery orders without a reservationId. |
| note | string | Optional | Order-level note. |
| paymentEndpoint | object | Optional | url (string), headers (object of string→string) — your payment webhook, called at invoicing. Respond within 30 seconds or the invoice remains unpaid and requires manual processing. |
| payment | object | Optional | method: "OnAccount" | "CreditCard"; cardId (string, optional — a stored card id). |
customerIdstring (≤8 chars)Conditional- An existing customer account id. Provide
customerIdorcustomer. customerobjectConditional- Used when no
customerIdis provided. All fields within it are optional. customer.firstNamestringOptional- Customer first name.
customer.lastNamestringOptional- Customer last name.
customer.phonestringOptional- Customer phone number.
customer.emailstringOptional- Customer email address.
customer.addressobjectOptionaladdressLine1(≤30 chars),addressLine2,city,state,zipCode.externalOrderIdstring (≤20 chars)Optional- Your own order reference; must be unique per order. Used by
GET /orders/external/{externalOrderId}and returned in webhook calls. statusstringOptional- Initial order status:
"OrderEntered"or"Unpicked". itemsarrayRequired- Order lines. Must be non-empty.
items[].productCodestring (≤12 chars)Required- The item barcode.
items[].quantitynumberRequired- Units; for by-weight lines (
"Lbs"), the weight in pounds. items[].descriptionstring (≤30 chars)Optional- Line description.
items[].priceQtynumberOptional- A divisor that determines the actual unit price (
unitPrice ÷ priceQty). items[].unitOfMeasurestringOptional"Unit","Case", or"Lbs".items[].unitPricenumberOptional- Price per unit, in dollars.
items[].totalPricenumberOptional- Line total, in dollars.
items[].notestringOptional- Line note, visible to pickers and kitchen staff.
deliveryAddressobjectDelivery- The recipient of the order.
deliveryAddress.namestringOptional- Recipient name.
deliveryAddress.phoneNumberstringOptional- Recipient phone number.
deliveryAddress.addressobjectDeliveryaddressLine1(≤30 chars),addressLine2,city(required for delivery),state,zipCode.orderMethodstringOptional"Pickup"or"Delivery". Defaults to Delivery whendeliveryAddressis present.pickupDeliveryTimestringConditional- ISO 8601 date-time, Eastern time — the customer's requested time (see Delivery scheduling). Required for Delivery orders without a
reservationId. notestringOptional- Order-level note.
paymentEndpointobjectOptionalurl(string),headers(object of string→string) — your payment webhook, called at invoicing. Respond within 30 seconds or the invoice remains unpaid and requires manual processing.paymentobjectOptionalmethod:"OnAccount"|"CreditCard";cardId(string, optional — a stored card id).
Optional Evergreen extensions. Three extra body fields may be included alongside the schema above: reservationId (attach a previously reserved slot; confirmed automatically when the order is created), store (pickup orders only, e.g. "uptown"), and priority (integer 1 | 2 | 3, 1 = highest, omitted = normal — the store can use it to sequence work; other values return 400 invalid_request). A standard payload works as-is.
curl -X POST "https://api.filld.dev/api/v1/orders" \
-H "x-api-key: evg_live_XXXXXXXXXXXXXXXX" \
-H "X-Customer-Pin: 1234" \
-H "Content-Type: application/json" \
-d '{
"customerId": "18452",
"externalOrderId": "cart_88213",
"orderMethod": "Delivery",
"pickupDeliveryTime": "2026-07-07T10:00:00",
"items": [
{
"productCode": "412009",
"description": "Golden Flow OJ 64oz",
"quantity": 2,
"unitOfMeasure": "Unit",
"unitPrice": 4.99,
"totalPrice": 9.98
},
{
"productCode": "286748",
"description": "Chicken Cutlets",
"quantity": 3.5,
"unitOfMeasure": "Lbs",
"unitPrice": 8.99,
"note": "Sliced thin, please"
}
],
"deliveryAddress": {
"name": "Chaim Brachfeld",
"phoneNumber": "8455550142",
"address": {
"addressLine1": "12 Main St",
"city": "Monsey",
"state": "NY",
"zipCode": "10952"
}
},
"payment": { "method": "OnAccount" },
"paymentEndpoint": {
"url": "https://partner.example.com/webhooks/payment",
"headers": { "x-webhook-secret": "whsec_XXXXXXXX" }
},
"note": "Leave by the side door"
}'HTTP/1.1 201 Created
Location: /orders/id/448291
x-evergreen-reservation-id: res_9f2c7a1e4b8d
x-evergreen-delivery-time: 2026-07-07T12:00:00-04:00
x-evergreen-store: uptown
x-evergreen-window: 2026-07-07 09:00-12:00A successful create returns 201 Created with an empty body; the Location header carries the order number (its value is relative to the base URL). The additional x-evergreen-* headers carry scheduling and reservation info. Errors use problem+json — see Error Handling.
Response statuses
201Order created — empty body; the Location header carries the order number.400invalid_request— the body is malformed, or a delivery order withoutreservationIdhas a missing or unparseablepickupDeliveryTime.409slot_full— the delivery window covering the requested time is full; pick another time.401unauthorized— missing or invalid API key.429rate_limited— too many requests.OtherAny other error uses the same problem+json format. When an order cannot be created, any window capacity taken by the call is released; a reservation you referenced stays intact so you can retry.
Edit an Order
Auth: API key/api/v1/orders/id/{orderNumber}Edit an order while it is still editable — that is, while its tracking status is accepted (not yet released to fulfillment). Once the order moves on, edits return 409 order_not_editable("This order was already sent to the store and can no longer be edited."). All body fields are optional; send at least one.
| Parameter | Type | Required | Description |
|---|---|---|---|
| items | array | Optional | Full replacement for the order's lines — same line shape as create. Must be non-empty when present. |
| pickupDeliveryTime | string | Optional | ISO 8601 date-time, Eastern — moves the order's slot booking. Same acceptance policy as create, including the closed-window override. |
| note | string | Optional | Replaces the order-level note. |
| priority | integer | Optional | 1 | 2 | 3 (1 = highest, omitted = normal). Evergreen extension — the store can use it to sequence work. Other values return 400 invalid_request. |
itemsarrayOptional- Full replacement for the order's lines — same line shape as create. Must be non-empty when present.
pickupDeliveryTimestringOptional- ISO 8601 date-time, Eastern — moves the order's slot booking. Same acceptance policy as create, including the closed-window override.
notestringOptional- Replaces the order-level note.
priorityintegerOptional1|2|3(1 = highest, omitted = normal). Evergreen extension — the store can use it to sequence work. Other values return400 invalid_request.
curl -X PATCH "https://api.filld.dev/api/v1/orders/id/448291" \
-H "x-api-key: evg_live_XXXXXXXXXXXXXXXX" \
-H "Content-Type: application/json" \
-d '{
"pickupDeliveryTime": "2026-07-07T14:00:00",
"note": "Customer added items",
"items": [
{ "productCode": "412009", "quantity": 3, "unitPrice": 4.99 },
{ "productCode": "573301", "quantity": 1, "unitPrice": 6.49 }
]
}'{
"orderNumber": "448291",
"status": "Picking",
"orderMethod": "Delivery",
"pickupDeliveryTime": "2026-07-07T12:00:00-04:00",
"customerId": 18452,
"externalOrderId": "cart_88213",
"items": [
{ "productCode": "412009", "quantity": 2, "unitPrice": 4.99 },
{ "productCode": "286748", "quantity": 0, "unitPrice": 12.50, "note": "Out of stock" }
],
"total": 9.98,
"tracking": {
"status": "picking",
"scnNumber": "SCN1043",
"posOrderId": "448291",
"invoiceNumber": null,
"eta": null,
"driver": null,
"trackUrl": null,
"receiptUrl": null,
"deliveredAt": null,
"timeline": [
{ "status": "accepted", "at": "2026-07-06T18:02:11.000Z" },
{ "status": "sent_to_pos", "at": "2026-07-06T18:05:09.000Z" },
{ "status": "picking", "at": "2026-07-07T13:10:44.000Z" }
]
}
}The response is the updated order, the same shape as GET /orders/id/{orderNumber} — check pickupDeliveryTime and tracking on it. Orders created with a test key are treated as sent immediately, so sandbox edits return order_not_editable.
Response statuses
200Order updated.400invalid_request— a baditemsarray, an unparseablepickupDeliveryTime, or apriorityoutside 1–3.409order_not_editable— the order was already sent to the store and can no longer be edited.404not_found— the order was not created through this API, or does not belong to your organization.401unauthorized— missing or invalid API key.
List & Track Orders
Auth: API key/api/v1/ordersBatch tracking for all your orders — the newest 200, with each order's normalized status, window, ETA, driver, and full timeline. Poll with since=<your last poll time> every minute or two to receive only the orders whose tracking changed — the cheapest way to stay current.
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | Optional | Filter by lifecycle status: accepted, sent_to_pos, picking, checked_out, label_created, out_for_delivery, delivered, or cancelled. |
| date | string | Optional | Filter by delivery date, YYYY-MM-DD. |
| since | string | Optional | ISO 8601 instant — return only orders whose tracking changed after it. Pass your last poll time for cheap incremental updates. |
statusstringOptional- Filter by lifecycle status:
accepted,sent_to_pos,picking,checked_out,label_created,out_for_delivery,delivered, orcancelled. datestringOptional- Filter by delivery date,
YYYY-MM-DD. sincestringOptional- ISO 8601 instant — return only orders whose tracking changed after it. Pass your last poll time for cheap incremental updates.
curl "https://api.filld.dev/api/v1/orders?since=2026-07-07T13:00:00Z" \
-H "x-api-key: evg_live_XXXXXXXXXXXXXXXX"{
"orders": [
{
"orderNumber": "448291",
"externalOrderId": "cart_88213",
"scnNumber": "SCN1043",
"posOrderId": "448291",
"invoiceNumber": "774612",
"orderMethod": "Delivery",
"status": "out_for_delivery",
"window": { "date": "2026-07-07", "start": "09:00", "end": "12:00" },
"scheduledTime": "2026-07-07T09:00:00",
"slotOverride": null,
"priority": 1,
"itemCount": 12,
"eta": "2026-07-07T14:42:00.000Z",
"driver": "Moshe",
"trackUrl": "https://filld.link/t/8f2ac91d",
"deliveredAt": null,
"timeline": [
{ "status": "accepted", "at": "2026-07-06T18:02:11.000Z" },
{ "status": "sent_to_pos", "at": "2026-07-06T18:05:09.000Z" },
{ "status": "picking", "at": "2026-07-07T11:10:44.000Z" },
{ "status": "checked_out", "at": "2026-07-07T12:20:03.000Z" },
{ "status": "label_created", "at": "2026-07-07T12:41:27.000Z" },
{ "status": "out_for_delivery", "at": "2026-07-07T13:05:12.000Z" }
],
"editedAt": null,
"createdAt": "2026-07-06T18:02:11.000Z",
"updatedAt": "2026-07-07T13:05:12.000Z"
}
]
}slotOverride is set when the order was accepted into a closed window ("full" | "deadline" | "unscheduled", or combined); priority (1 | 2 | 3, 1 = highest) is present only when it was set on the order; eta, driver, and trackUrl fill in once the order is out for delivery.
Three reference ids ride along: scnNumber — our internal order reference (e.g. "SCN1043"), assigned when the order is accepted; useful when talking to the store about an order. posOrderId — the fulfillment system's order id, present once the order has been sent (for orders accepted while held it can differ from your orderNumber — your orderNumber never changes). invoiceNumber — the invoice id, present once the order is checked out.
Response statuses
200Orders returned (newest 200).401unauthorized— missing or invalid API key.429rate_limited— too many requests.
Batch Tracking
Auth: API key/api/v1/orders/batchBatch order tracking by id. Send up to 200 ids per call — matched against the order number, the SCN number, or your externalOrderId, mixed freely — and get back exactly those orders, in the same row shape as GET /api/v1/orders. Ids that don't match anything are simply absent (no error), and an order referenced by two of its ids returns once.
| Parameter | Type | Required | Description |
|---|---|---|---|
| orderIds | array of strings | Required | Up to 200 ids, matched against the order number, the SCN number, or your externalOrderId — mix freely. |
orderIdsarray of stringsRequired- Up to 200 ids, matched against the order number, the SCN number, or your
externalOrderId— mix freely.
curl -X POST "https://api.filld.dev/api/v1/orders/batch" \
-H "x-api-key: evg_live_XXXXXXXXXXXXXXXX" \
-H "Content-Type: application/json" \
-d '{
"orderIds": ["94021133", "SCN1043", "cart_88213"]
}'{
"orders": [
{
"orderNumber": "448291",
"externalOrderId": "cart_88213",
"scnNumber": "SCN1043",
"posOrderId": "448291",
"invoiceNumber": "774612",
"orderMethod": "Delivery",
"status": "out_for_delivery",
"window": { "date": "2026-07-07", "start": "09:00", "end": "12:00" },
"scheduledTime": "2026-07-07T09:00:00",
"slotOverride": null,
"itemCount": 12,
"eta": "2026-07-07T14:42:00.000Z",
"driver": "Moshe",
"trackUrl": "https://filld.link/t/8f2ac91d",
"deliveredAt": null,
"timeline": [
{ "status": "accepted", "at": "2026-07-06T18:02:11.000Z" },
{ "status": "sent_to_pos", "at": "2026-07-06T18:05:09.000Z" },
{ "status": "picking", "at": "2026-07-07T11:10:44.000Z" },
{ "status": "checked_out", "at": "2026-07-07T12:20:03.000Z" },
{ "status": "label_created", "at": "2026-07-07T12:41:27.000Z" },
{ "status": "out_for_delivery", "at": "2026-07-07T13:05:12.000Z" }
],
"editedAt": null,
"createdAt": "2026-07-06T18:02:11.000Z",
"updatedAt": "2026-07-07T13:05:12.000Z"
},
{
"orderNumber": "94021133",
"externalOrderId": null,
"scnNumber": "SCN1044",
"posOrderId": null,
"invoiceNumber": null,
"orderMethod": "Delivery",
"status": "accepted",
"window": { "date": "2026-07-08", "start": "14:00", "end": "17:00" },
"scheduledTime": "2026-07-08T14:00:00",
"slotOverride": null,
"itemCount": 4,
"eta": null,
"driver": null,
"trackUrl": null,
"deliveredAt": null,
"timeline": [
{ "status": "accepted", "at": "2026-07-07T15:12:40.000Z" }
],
"editedAt": null,
"createdAt": "2026-07-07T15:12:40.000Z",
"updatedAt": "2026-07-07T15:12:40.000Z"
}
]
}Here "SCN1043" and "cart_88213" both reference order 448291, so three ids return two rows.
Response statuses
200Matching orders returned; ids that match nothing are simply absent.400invalid_request—orderIdsmissing, empty, containing non-strings, or more than 200 ids.401unauthorized— missing or invalid API key.429rate_limited— too many requests.
Get Order Status
Auth: API key/api/v1/orders/id/{orderNumber}Fetch an order by its order number — the value from the create response's Location header. The response reflects the order as it stands: after picking, items reflect what was actually filled. It includes a tracking object — normalized status, scnNumber, posOrderId, invoiceNumber, eta, driver, trackUrl, receiptUrl, deliveredAt, and the full timeline. Only orders created through this API resolve; anything else returns 404 not_found.
| Parameter | Type | Required | Description |
|---|---|---|---|
| orderNumber | string | Required | The order number from the create response's Location header. |
orderNumberstringRequired- The order number from the create response's Location header.
curl "https://api.filld.dev/api/v1/orders/id/448291" \
-H "x-api-key: evg_live_XXXXXXXXXXXXXXXX"{
"orderNumber": "448291",
"status": "Picking",
"orderMethod": "Delivery",
"pickupDeliveryTime": "2026-07-07T12:00:00-04:00",
"customerId": 18452,
"externalOrderId": "cart_88213",
"items": [
{ "productCode": "412009", "quantity": 2, "unitPrice": 4.99 },
{ "productCode": "286748", "quantity": 0, "unitPrice": 12.50, "note": "Out of stock" }
],
"total": 9.98,
"tracking": {
"status": "picking",
"scnNumber": "SCN1043",
"posOrderId": "448291",
"invoiceNumber": null,
"eta": null,
"driver": null,
"trackUrl": null,
"receiptUrl": null,
"deliveredAt": null,
"timeline": [
{ "status": "accepted", "at": "2026-07-06T18:02:11.000Z" },
{ "status": "sent_to_pos", "at": "2026-07-06T18:05:09.000Z" },
{ "status": "picking", "at": "2026-07-07T13:10:44.000Z" }
]
}
}Response statuses
200The order as it currently stands.404not_found— the order was not created through this API, or does not belong to your organization.502pos_unreachable— the order could not be retrieved right now. Safe to retry.401unauthorized— missing or invalid API key.
Lookup by External Id
Auth: API key/api/v1/orders/external/{externalOrderId}Fetch an order by your own externalOrderId — the value you sent on the create call. Identical response shape to the id lookup, including the tracking object. Only orders created through this API resolve.
| Parameter | Type | Required | Description |
|---|---|---|---|
| externalOrderId | string | Required | Your order id, as sent in the create body's externalOrderId field. |
externalOrderIdstringRequired- Your order id, as sent in the create body's externalOrderId field.
curl "https://api.filld.dev/api/v1/orders/external/cart_88213" \
-H "x-api-key: evg_live_XXXXXXXXXXXXXXXX"{
"orderNumber": "448291",
"status": "Picking",
"orderMethod": "Delivery",
"pickupDeliveryTime": "2026-07-07T12:00:00-04:00",
"customerId": 18452,
"externalOrderId": "cart_88213",
"items": [
{ "productCode": "412009", "quantity": 2, "unitPrice": 4.99 },
{ "productCode": "286748", "quantity": 0, "unitPrice": 12.50, "note": "Out of stock" }
],
"total": 9.98,
"tracking": {
"status": "picking",
"scnNumber": "SCN1043",
"posOrderId": "448291",
"invoiceNumber": null,
"eta": null,
"driver": null,
"trackUrl": null,
"receiptUrl": null,
"deliveredAt": null,
"timeline": [
{ "status": "accepted", "at": "2026-07-06T18:02:11.000Z" },
{ "status": "sent_to_pos", "at": "2026-07-06T18:05:09.000Z" },
{ "status": "picking", "at": "2026-07-07T13:10:44.000Z" }
]
}
}Response statuses
200The order as it currently stands.404not_found— no order with thatexternalOrderIdwas created through this API by your organization.502pos_unreachable— the order could not be retrieved right now. Safe to retry.401unauthorized— missing or invalid API key.
Error Handling
Errors come in two formats, matching each surface of the API:
- Order endpoints (
/orders…) return RFC 7807 problem+json —{ type, title, status, detail, code }— wherecodeis the machine-readable code. - Slot endpoints (
/api/v1/delivery/…) return the{ error, message }envelope.
{
"type": "https://httpproblems.com/http-status/409",
"title": "Conflict",
"status": 409,
"detail": "This delivery window is no longer available. Fetch availability again for current options.",
"code": "slot_unavailable"
}{
"error": "slot_unavailable",
"message": "This delivery window is no longer available. Fetch availability again for current options."
}| HTTP status | Error codes | Meaning |
|---|---|---|
| 200 / 201 | — | Success. |
| 400 | invalid_request, invalid_slot_id | The request body, query, or slot token is malformed. |
| 401 | unauthorized | Missing or invalid x-api-key header. |
| 404 | not_found | No such reservation or proxied order in your organization. |
| 409 | slot_unavailable, order_by_passed, reservation_expired, reservation_cancelled, order_not_editable | The request conflicts with the current state of the slot, reservation, or order. |
| 422 | address_not_covered, zone_mismatch | The address is outside the delivery area, or the slot does not serve it. |
| 429 | rate_limited | Too many requests. Back off and retry. |
| 500 | internal_error | Something went wrong on Evergreen's side. Safe to retry. |
| 502 | pos_unreachable | The POS returned an error or could not be reached. Safe to retry. |
The codes above appear as code in problem+json on the order endpoints and as error in the envelope on the delivery endpoints.
Expect slot_unavailable under load
409 slot_unavailable at reserve time is normal when multiple platforms draw from the same capacity pool. Re-fetch availability and offer the customer the remaining windows.Best Practices
- Reserve when the order is placed, not at slot display. Showing slots does not book them, and a reservation consumes real capacity immediately.
- Attach your order number. After placing the order, call confirm with
posOrderRefso the store can reconcile the reservation against the order. - Release bookings you no longer need.
DELETEthe reservation if the order falls through or the customer switches windows. (In opt-in hold mode, unconfirmed holds expire on their own — confirm beforeexpiresAtand keep one active hold per cart.) - Handle slot_unavailable gracefully by re-fetching availability and presenting the remaining windows.
- Respect orderBy. Hide windows client-side once the deadline nears, rather than letting the reserve call fail.
- Send the full street address, not just a ZIP — polygon coverage is more precise than ZIP-level coverage.
Support
For integration questions, contact Filld to get API keys and a test key for development. Include your organization name so your keys can be provisioned against the right account.