Filldv1

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:

Base URL
https://api.filld.dev/api/v1

Order 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.

Header
x-api-key: evg_live_XXXXXXXXXXXXXXXX

Keep your key server-side

Never embed your API key in a browser or mobile app. Route all Partner Slots API calls through your own backend.

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: 201 with an empty body, a Location: /orders/id/TEST-48210937 header, and the extra header x-evergreen-sandbox: true.
  • Failure simulation. Include the string simulate=pos_error anywhere in the order's note field — the sandbox returns a 400 error 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 a TEST- 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, so PATCH /orders/id/… returns 409 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.

Request
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"
      }
    }
  }'
Response — 201 Created (simulated, empty body)
HTTP/1.1 201 Created
Location: /orders/id/TEST-48210937
x-evergreen-sandbox: true

Sandbox availability is real availability

If a window shows as full in the sandbox, it really is full — pick another window rather than treating it as a test artifact.

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.

  1. Customer enters their address — check availability (POST /delivery/availability).
  2. 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.
  3. Optional: attach your POS order number via POST /reservations/{id}/confirm; call DELETE if your order failed.

Important

Reserve when the order is placed, not when the slots are displayed — a reservation consumes real capacity immediately.

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).

Request
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"
    }
  }'
Response — 201 Created
{
  "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 without ttlMinutes returns confirmed immediately, with expiresAt: null.
  • held — opt-in hold mode only: capacity is taken, but the reservation must be confirmed before expiresAt.
  • 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:

  1. Base URL: https://api.poswithlogic.dev https://api.filld.dev/api/v1
  2. 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

Check availability before every order. Accepting into a closed window is an override the store sees — use it deliberately, not as the default path.

Important

Always show the customer the window, never the scheduled time.

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 true on 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; an eta becomes 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.

  1. Customer picks a delivery time in your checkout.
  2. When the order is placed — one POST /reservations call with the address, the time, and (optionally) posOrderRef → booked; send the order to the POS as usual.
  3. Track the order — poll GET /api/v1/orders with since=… (see Order Tracking).

Availability-first

Show customers our real windows and order-by deadlines, then book the one they pick.

  1. Customer enters their address — send POST /availability.
  2. Customer picks a window and places the order — send POST /reservations with the slotId to book the slot, then send the order to the POS exactly as you do today.
  3. Optional: attach your POS order number via POST /reservations/{id}/confirm; DELETE the reservation if your order failed.
  4. Track the order — poll GET /api/v1/orders with since=….

Drop-in migration (recommended)

Change the base URL and the API key — keep sending orders exactly as you do today.

  1. Set your integration's base URL to https://api.filld.dev/api/v1 and swap x-api-key to your Evergreen key.
  2. Keep sending POST /orders with your existing payloads — the delivery window is validated and booked in the same call.
  3. Optionally adopt POST /api/v1/delivery/availability and /reservations for a better slot-picking UX — hold a window at checkout start and pass it via the optional reservationId extension field on the order.
  4. Track your orders — poll GET /api/v1/orders with since=….

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
POST/api/v1/delivery/availability

Check 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.

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

A GET variant is available for quick testing: GET /api/v1/delivery/availability?street=&city=&state=&zip=. Use the POST form in production.
Request
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"
    }
  }'
Response — 200 OK
{
  "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.

  • slotId is an opaque token. Pass it back verbatim when reserving; do not parse it or construct your own.
  • availability is "available" or "limited" (only a few spots left).
  • orderBy is 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
POST/api/v1/delivery/reservations

Books 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.

slotIdstringConditional
The slot token returned by /availability, passed back verbatim. Provide slotId or time.
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 slotId or time.
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.
Request
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"
  }'
Response — 201 Created
{
  "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, when ttlMinutes is passed).
  • 400invalid_slot_id — the slot token is malformed. invalid_request — the body is malformed, or neither slotId nor a parseable time was provided.
  • 409slot_unavailable — the window is full or no longer offered, or no delivery window covers the requested time; re-fetch availability. order_by_passed— the window's orderBy deadline 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
GET/api/v1/delivery/reservations

List your organization's reservations, most recent first. Returns up to 100 reservations.

statusstringOptional
Filter by status: one of held, confirmed, cancelled, expired.
datestringOptional
Filter by delivery date, YYYY-MM-DD (store-local).
Request
curl "https://api.filld.dev/api/v1/delivery/reservations?status=confirmed&date=2026-07-07" \
  -H "x-api-key: evg_live_XXXXXXXXXXXXXXXX"
Response — 200 OK
{
  "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
GET/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.

idstringRequired
The reservationId returned when the reservation was created.
Request
curl "https://api.filld.dev/api/v1/delivery/reservations/res_9f2c7a1e4b8d" \
  -H "x-api-key: evg_live_XXXXXXXXXXXXXXXX"
Response — 200 OK
{
  "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
POST/api/v1/delivery/reservations/{id}/confirm

Attach 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.

posOrderRefstringOptional
Your order number for the placed order. Strongly recommended.
partnerOrderRefstringOptional
Your order or cart identifier.
customerRefstringOptional
Your identifier for the customer.
Request
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"
  }'
Response — 200 OK
{
  "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
DELETE/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.

idstringRequired
The reservationId to cancel.
Request
curl -X DELETE "https://api.filld.dev/api/v1/delivery/reservations/res_9f2c7a1e4b8d" \
  -H "x-api-key: evg_live_XXXXXXXXXXXXXXXX"
Response — 200 OK
{
  "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
POST/api/v1/orders

Create an order. Returns 201 Created with a Location header carrying the order number; the delivery window is validated and booked in the same call.

x-api-keystringRequired
Your API key.
X-Customer-PinstringConditional
Required when payment.method is "OnAccount" or when paying with a stored card.
Content-TypestringRequired
application/json.
customerIdstring (≤8 chars)Conditional
An existing customer account id. Provide customerId or customer.
customerobjectConditional
Used when no customerId is 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.addressobjectOptional
addressLine1 (≤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.addressobjectDelivery
addressLine1 (≤30 chars), addressLine2, city (required for delivery), state, zipCode.
orderMethodstringOptional
"Pickup" or "Delivery". Defaults to Delivery when deliveryAddress is 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.
paymentEndpointobjectOptional
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.
paymentobjectOptional
method: "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.

Request
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"
  }'
Response — 201 Created (empty body)
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:00

A 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 without reservationId has a missing or unparseable pickupDeliveryTime.
  • 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
PATCH/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.

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.
priorityintegerOptional
1 | 2 | 3 (1 = highest, omitted = normal). Evergreen extension — the store can use it to sequence work. Other values return 400 invalid_request.
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 }
    ]
  }'
Response — 200 OK (the updated order)
{
  "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 bad items array, an unparseable pickupDeliveryTime, or a priority outside 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
GET/api/v1/orders

Batch 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.

statusstringOptional
Filter by lifecycle status: accepted, sent_to_pos, picking, checked_out, label_created, out_for_delivery, delivered, or cancelled.
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.
Request
curl "https://api.filld.dev/api/v1/orders?since=2026-07-07T13:00:00Z" \
  -H "x-api-key: evg_live_XXXXXXXXXXXXXXXX"
Response — 200 OK
{
  "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
POST/api/v1/orders/batch

Batch 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.

orderIdsarray of stringsRequired
Up to 200 ids, matched against the order number, the SCN number, or your externalOrderId — mix freely.
Request
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"]
  }'
Response — 200 OK
{
  "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 orderIds missing, 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
GET/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.

orderNumberstringRequired
The order number from the create response's Location header.
Request
curl "https://api.filld.dev/api/v1/orders/id/448291" \
  -H "x-api-key: evg_live_XXXXXXXXXXXXXXXX"
Response — 200 OK
{
  "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
GET/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.

externalOrderIdstringRequired
Your order id, as sent in the create body's externalOrderId field.
Request
curl "https://api.filld.dev/api/v1/orders/external/cart_88213" \
  -H "x-api-key: evg_live_XXXXXXXXXXXXXXXX"
Response — 200 OK
{
  "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 that externalOrderId was 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 } — where code is the machine-readable code.
  • Slot endpoints (/api/v1/delivery/…) return the { error, message } envelope.
Order endpoints — RFC 7807 problem+json
{
  "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"
}
Delivery endpoints — error envelope
{
  "error": "slot_unavailable",
  "message": "This delivery window is no longer available. Fetch availability again for current options."
}
HTTP statusError codesMeaning
200 / 201Success.
400invalid_request, invalid_slot_idThe request body, query, or slot token is malformed.
401unauthorizedMissing or invalid x-api-key header.
404not_foundNo such reservation or proxied order in your organization.
409slot_unavailable, order_by_passed, reservation_expired, reservation_cancelled, order_not_editableThe request conflicts with the current state of the slot, reservation, or order.
422address_not_covered, zone_mismatchThe address is outside the delivery area, or the slot does not serve it.
429rate_limitedToo many requests. Back off and retry.
500internal_errorSomething went wrong on Evergreen's side. Safe to retry.
502pos_unreachableThe 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

A 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

  1. Reserve when the order is placed, not at slot display. Showing slots does not book them, and a reservation consumes real capacity immediately.
  2. Attach your order number. After placing the order, call confirm with posOrderRef so the store can reconcile the reservation against the order.
  3. Release bookings you no longer need. DELETE the reservation if the order falls through or the customer switches windows. (In opt-in hold mode, unconfirmed holds expire on their own — confirm before expiresAt and keep one active hold per cart.)
  4. Handle slot_unavailable gracefully by re-fetching availability and presenting the remaining windows.
  5. Respect orderBy. Hide windows client-side once the deadline nears, rather than letting the reserve call fail.
  6. 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.