Drops

94 operations. Every schema and example on this page is generated from the platform contract.

A store's batch drops.

Every drop for the authenticated store, newest first, each with its items, windows and live counters. Also returns whether the store can run a drop at all, and if not, why.

Parameters

store_idstring · uuidqueryrequired
include_pastbooleanqueryoptional

Include drops whose collection date has passed.

limitintegerqueryoptional

Response, 200

dropsarray of objectrequired
40 child fields
idstringrequired
store_idstringrequired
codestringrequired

The short code that identifies this drop.

shop_urlstring, nullablerequired

The shop's permanent page, at its own address: https://{handle}.membber.com/

image_urlstring, nullablerequired
share_urlstringrequired

The link to this specific drop.

namestringrequired
descriptionstring, nullablerequired
statusenumrequired
draftscheduledcancelled
stateenumrequired

Computed at read time. Never sent by a client.

draftscheduledliveclosedcollectedcancelled
opens_atstringrequired
closes_atstring, nullablerequired

Null only while a draft has no closing time yet. A scheduled drop always has one.

collection_datestringrequired
timezonestringrequired
checkout_ttl_secondsnumberrequired
menu_visible_before_openbooleanrequired
show_stock_countbooleanrequired
max_per_customernumber, nullablerequired
itemsarray of objectrequired
26 child fields
idstringrequired
menu_item_idstringrequired
namestringrequired

Copied from the menu item at read time, never stored twice. On a line that sells a size or colour: the item’s name with them, "Hoodie, M, Black".

item_namestringoptional

Her item’s own name, on a line that sells one of its sizes or colours (`name` then reads "Hoodie, M, Black").

size_labelstringoptional

The size this line sells, as her item names it. Absent: no size.

colour_labelstringoptional

The colour this line sells, as her item names it. Absent: no colour.

size_namestringoptional

Her own word for this item’s sizes ("Flavour"), when she chose one. Absent: the word for size.

colour_namestringoptional

Her own word for this item’s colours ("Grind"), when she chose one. Absent: the word for colour.

colour_photo_urlstringoptional

The photo she chose for this colour, one of the item’s own photos. Absent: none chosen.

price_pencenumber, nullablerequired

What one sells at in this drop, before Membber's fee: the drop's own price when it has one, else the menu price. What checkout charges.

usual_price_pencenumber, nullableoptional

Her usual (menu) price for one.

drop_price_penceinteger, nullableoptional

This drop's own price for one, or null when it sells at the usual price.

buyer_price_pencenumber, nullablerequired
image_urlstring, nullablerequired
quantitynumberrequired

How many were put up. Meaningless when is_unlimited.

remainingnumberrequired

How many are still available. Never negative. Mirrors quantity when is_unlimited.

is_unlimitedbooleanrequired
soldnumberrequired
max_per_ordernumber, nullablerequired

null = no limit.

per_bundleinteger, nullableoptional

How many of this item go in one box of a one-price drop. Null means one.

sold_out_atstring, nullablerequired

Always null for an unlimited item.

sort_ordernumberrequired
allergensarray of stringrequired
allergens_declared_nonebooleanrequired
allergens_missingbooleanrequired
is_foodbooleanoptional

False for an item that is not food (clothes, flowers): no allergen answer is asked of it. True or absent: food.

windowsarray of objectrequired
9 child fields
idstringrequired

Send it back on the windows PUT to edit this window in place.

starts_atstringrequired
ends_atstringrequired
max_ordersnumber, nullablerequired

null = no cap on orders in this window.

orders_remainingnumber, nullablerequired
booked_countintegeroptional

People still to collect from this window. Moving it needs confirm_booked_changes; removing it is refused.

booked_namesarray of stringoptional

First names of the people still to collect, oldest order first.

removablebooleanoptional

False when anyone holds a place in this window, collected or not: removing it is refused.

in_disputebooleanoptional

True when the only orders holding this window are disputed ones nobody collected. Absent otherwise.

moves_not_reachedobjectoptional

Buyers still to collect whom a moved collection time could not reach. Absent when there are none.

2 child fields
not_toldintegerrequired

How many buyers could not be reached about a moved time.

not_told_namesarray of stringoptional

Their first names, oldest order first, three at most (the most the sentence names). Shorter than not_told when one gave no name or there are more than three; absent when none gave one.

buyersarray of objectoptional
21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

cancelled_buyersarray of objectoptional

Orders refunded in full, disputed by the buyer, released unpaid or cancelled with the drop, newest first. Listed only: never in buyers and never in any count or total.

21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

orders_countnumberrequired
units_soldnumberrequired
units_totalnumberrequired

Sum of the counted items only; an unlimited item adds nothing.

has_unlimitedbooleanrequired
gross_pencenumberrequired

TAKEN: money that has arrived and stayed, from every paid status (paid, ready, collected, part-refunded), net of recorded refunds. Card orders without the buyer fee; cash and transfer in full.

still_to_come_pencenumberrequired
unpaid_orders_countnumberrequired
bundle_price_pencenumber, nullableoptional
bundle_buyer_price_pencenumber, nullableoptional
bundle_totalnumber, nullableoptional

How many boxes were put up.

bundle_remainingnumber, nullableoptional

How many boxes are left. Stock, never writable.

is_bundlebooleanoptional

True when this drop sells one box at one price.

blockersarray of stringrequired
blocker_codesarray of objectoptional
2 child fields
codeenumrequired
no_itemsno_timesno_closesno_quantityno_priceno_allergenstime_before_openstime_before_closestime_off_day
namesarray of stringrequired
making_started_atstring, nullableoptional
created_atstringrequired
updated_atstringrequired
live_drop_idstring, nullablerequired
drops_livebooleanrequired
blocked_reasonenumrequired

Why a drop cannot open. Say this to the merchant, do not refuse blankly.

no_addoncollect_offno_paymentspayments_destinationunavailable
shop_liveboolean, nullablerequired

Whether the shop is live. A drop can only be scheduled on a live shop. Null when unreadable.

card_practice_onlybooleanrequired

Pay now is on but cards are not set up yet: this real shop is still on its old practice Stripe account, which takes no card. "Set up cards" makes its live account.

payment_methodsarray of enumrequired
pay_nowbank_transfercash
reachable_customersinteger, nullablerequired
page_viewsobjectrequired

Visits to this shop's public drops pages. Null only when the count could not be read.

4 child fields
shop_todayintegerrequired
shop_last_7_daysintegerrequired
drops_todayintegerrequired
drops_last_7_daysintegerrequired
downloads_enabledbooleanoptional

True while downloads are switched on: a seller screen then shows its Downloads row. Absent otherwise.

curl -G "https://www.membber.com/api/v1/drops" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066"
Response, 200
{
  "drops": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "store_id": "6659c139-0000-4000-8000-d0c500000066",
      "code": "EXAMPLE10",
      "shop_url": "https://example.com/image.jpg",
      "image_url": "https://example.com/image.jpg",
      "share_url": "https://example.com/image.jpg",
      "name": "Example name",
      "description": "Added at the front desk",
      "status": "draft",
      "state": "draft",
      "opens_at": "<opens_at>",
      "closes_at": "<closes_at>",
      "collection_date": "<collection_date>",
      "timezone": "Europe/London",
      "checkout_ttl_seconds": 1,
      "menu_visible_before_open": true,
      "show_stock_count": true,
      "max_per_customer": 1,
      "items": [
        {
          "id": "00000d1b-0000-4000-8000-d0c500000000",
          "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
          "name": "Example name",
          "item_name": "<item_name>",
          "size_label": "<size_label>",
          "colour_label": "<colour_label>",
          "size_name": "<size_name>",
          "colour_name": "<colour_name>",
          "colour_photo_url": "https://example.com/image.jpg",
          "price_pence": 1500,
          "usual_price_pence": 1500,
          "drop_price_pence": 1500,
          "buyer_price_pence": 1500,
          "image_url": "https://example.com/image.jpg",
          "quantity": 1,
          "remaining": 1,
          "is_unlimited": true,
          "sold": 1,
          "max_per_order": 1,
          "per_bundle": -9007199254740991,
          "sold_out_at": "<sold_out_at>",
          "sort_order": 1,
          "allergens": [
            "<allergen>"
          ],
          "allergens_declared_none": true,
          "allergens_missing": true,
          "is_food": true
        }
      ],
      "windows": [
        {
          "id": "00000d1b-0000-4000-8000-d0c500000000",
          "starts_at": "<starts_at>",
          "ends_at": "<ends_at>",
          "max_orders": 1,
          "orders_remaining": 1,
          "booked_count": 1,
          "booked_names": [
            "<booked_name>"
          ],
          "removable": true,
          "in_dispute": true
        }
      ],
      "moves_not_reached": {
        "not_told": -9007199254740991,
        "not_told_names": [
          "<not_told_name>"
        ]
      },
      "buyers": [
        {
          "id": "00000d1b-0000-4000-8000-d0c500000000",
          "order_number": "<order_number>",
          "name": "Example name",
          "email": "alex@example.com",
          "total_pence": 1500,
          "paid_at": "<paid_at>",
          "collected_at": "<collected_at>",
          "fulfilment_status": "<fulfilment_status>",
          "collection_at": "<collection_at>",
          "status": "<status>",
          "payment_method_kind": "<payment_method_kind>",
          "offline_payment_due_at": "<offline_payment_due_at>",
          "offline_payment_recorded_at": "<offline_payment_recorded_at>",
          "buyer_fee_pence": 1500,
          "application_fee_pence": 1500,
          "refunded_pence": 1500,
          "stripe_mode": "<stripe_mode>",
          "added_by_shop": true,
          "failure_code": "EXAMPLE10",
          "dispute_due_by": "<dispute_due_by>",
          "dispute_lost_pence": 1500
        }
      ],
      "cancelled_buyers": [
        {
          "id": "00000d1b-0000-4000-8000-d0c500000000",
          "order_number": "<order_number>",
          "name": "Example name",
          "email": "alex@example.com",
          "total_pence": 1500,
          "paid_at": "<paid_at>",
          "collected_at": "<collected_at>",
          "fulfilment_status": "<fulfilment_status>",
          "collection_at": "<collection_at>",
          "status": "<status>",
          "payment_method_kind": "<payment_method_kind>",
          "offline_payment_due_at": "<offline_payment_due_at>",
          "offline_payment_recorded_at": "<offline_payment_recorded_at>",
          "buyer_fee_pence": 1500,
          "application_fee_pence": 1500,
          "refunded_pence": 1500,
          "stripe_mode": "<stripe_mode>",
          "added_by_shop": true,
          "failure_code": "EXAMPLE10",
          "dispute_due_by": "<dispute_due_by>",
          "dispute_lost_pence": 1500
        }
      ],
      "orders_count": 1,
      "units_sold": 1,
      "units_total": 1,
      "has_unlimited": true,
      "gross_pence": 1500,
      "still_to_come_pence": 1500,
      "unpaid_orders_count": 1,
      "bundle_price_pence": 1500,
      "bundle_buyer_price_pence": 1500,
      "bundle_total": 1,
      "bundle_remaining": 1,
      "is_bundle": true,
      "blockers": [
        "<blocker>"
      ],
      "blocker_codes": [
        {
          "code": "no_items",
          "names": [
            "Example name"
          ]
        }
      ],
      "making_started_at": "<making_started_at>",
      "created_at": "<created_at>",
      "updated_at": "<updated_at>"
    }
  ],
  "live_drop_id": "68f0d418-0000-4000-8000-d0c500000068",
  "drops_live": true,
  "blocked_reason": "no_addon",
  "shop_live": true,
  "card_practice_only": true,
  "payment_methods": [
    "pay_now"
  ],
  "reachable_customers": -9007199254740991,
  "page_views": {
    "shop_today": -9007199254740991,
    "shop_last_7_days": -9007199254740991,
    "drops_today": -9007199254740991,
    "drops_last_7_days": -9007199254740991
  },
  "downloads_enabled": true
}

Create a drop.

Creates a DRAFT drop for the authenticated store. A draft is invisible to customers and has no link until it is scheduled. Send a `client_token` to make the create idempotent.

Request body

store_idstring · uuidrequired
client_tokenstringoptional

Send the same token on a retry to get the SAME drop back instead of a duplicate.

namestringrequired
descriptionstring, nullableoptional
opens_atstringrequired

ISO instant. The drop opens itself at this minute.

closes_atstring, nullableoptional

ISO instant. Null is allowed on a DRAFT (nothing is on sale yet, so nothing closes), but a drop cannot be scheduled without a closing time. It may be sent in the same PATCH as status=scheduled.

collection_datestringrequired

YYYY-MM-DD in the store timezone.

bundle_price_penceinteger, nullableoptional

What the whole box costs, in pence. Null means this is not a box drop.

bundle_buyer_price_pencenumber, nullableoptional
bundle_totalinteger, nullableoptional

How many boxes exist. Null means this is not a box drop.

timezonestringoptional
checkout_ttl_secondsanyoptional

Basket hold, in seconds. Defaults to 300 (5 minutes).

menu_visible_before_openbooleanoptional
show_stock_countbooleanoptional
max_per_customerinteger, nullableoptional

null = no limit.

image_urlstring, nullableoptional

Response, 200

dropobjectrequired
40 child fields
idstringrequired
store_idstringrequired
codestringrequired

The short code that identifies this drop.

shop_urlstring, nullablerequired

The shop's permanent page, at its own address: https://{handle}.membber.com/

image_urlstring, nullablerequired
share_urlstringrequired

The link to this specific drop.

namestringrequired
descriptionstring, nullablerequired
statusenumrequired
draftscheduledcancelled
stateenumrequired

Computed at read time. Never sent by a client.

draftscheduledliveclosedcollectedcancelled
opens_atstringrequired
closes_atstring, nullablerequired

Null only while a draft has no closing time yet. A scheduled drop always has one.

collection_datestringrequired
timezonestringrequired
checkout_ttl_secondsnumberrequired
menu_visible_before_openbooleanrequired
show_stock_countbooleanrequired
max_per_customernumber, nullablerequired
itemsarray of objectrequired
26 child fields
idstringrequired
menu_item_idstringrequired
namestringrequired

Copied from the menu item at read time, never stored twice. On a line that sells a size or colour: the item’s name with them, "Hoodie, M, Black".

item_namestringoptional

Her item’s own name, on a line that sells one of its sizes or colours (`name` then reads "Hoodie, M, Black").

size_labelstringoptional

The size this line sells, as her item names it. Absent: no size.

colour_labelstringoptional

The colour this line sells, as her item names it. Absent: no colour.

size_namestringoptional

Her own word for this item’s sizes ("Flavour"), when she chose one. Absent: the word for size.

colour_namestringoptional

Her own word for this item’s colours ("Grind"), when she chose one. Absent: the word for colour.

colour_photo_urlstringoptional

The photo she chose for this colour, one of the item’s own photos. Absent: none chosen.

price_pencenumber, nullablerequired

What one sells at in this drop, before Membber's fee: the drop's own price when it has one, else the menu price. What checkout charges.

usual_price_pencenumber, nullableoptional

Her usual (menu) price for one.

drop_price_penceinteger, nullableoptional

This drop's own price for one, or null when it sells at the usual price.

buyer_price_pencenumber, nullablerequired
image_urlstring, nullablerequired
quantitynumberrequired

How many were put up. Meaningless when is_unlimited.

remainingnumberrequired

How many are still available. Never negative. Mirrors quantity when is_unlimited.

is_unlimitedbooleanrequired
soldnumberrequired
max_per_ordernumber, nullablerequired

null = no limit.

per_bundleinteger, nullableoptional

How many of this item go in one box of a one-price drop. Null means one.

sold_out_atstring, nullablerequired

Always null for an unlimited item.

sort_ordernumberrequired
allergensarray of stringrequired
allergens_declared_nonebooleanrequired
allergens_missingbooleanrequired
is_foodbooleanoptional

False for an item that is not food (clothes, flowers): no allergen answer is asked of it. True or absent: food.

windowsarray of objectrequired
9 child fields
idstringrequired

Send it back on the windows PUT to edit this window in place.

starts_atstringrequired
ends_atstringrequired
max_ordersnumber, nullablerequired

null = no cap on orders in this window.

orders_remainingnumber, nullablerequired
booked_countintegeroptional

People still to collect from this window. Moving it needs confirm_booked_changes; removing it is refused.

booked_namesarray of stringoptional

First names of the people still to collect, oldest order first.

removablebooleanoptional

False when anyone holds a place in this window, collected or not: removing it is refused.

in_disputebooleanoptional

True when the only orders holding this window are disputed ones nobody collected. Absent otherwise.

moves_not_reachedobjectoptional

Buyers still to collect whom a moved collection time could not reach. Absent when there are none.

2 child fields
not_toldintegerrequired

How many buyers could not be reached about a moved time.

not_told_namesarray of stringoptional

Their first names, oldest order first, three at most (the most the sentence names). Shorter than not_told when one gave no name or there are more than three; absent when none gave one.

buyersarray of objectoptional
21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

cancelled_buyersarray of objectoptional

Orders refunded in full, disputed by the buyer, released unpaid or cancelled with the drop, newest first. Listed only: never in buyers and never in any count or total.

21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

orders_countnumberrequired
units_soldnumberrequired
units_totalnumberrequired

Sum of the counted items only; an unlimited item adds nothing.

has_unlimitedbooleanrequired
gross_pencenumberrequired

TAKEN: money that has arrived and stayed, from every paid status (paid, ready, collected, part-refunded), net of recorded refunds. Card orders without the buyer fee; cash and transfer in full.

still_to_come_pencenumberrequired
unpaid_orders_countnumberrequired
bundle_price_pencenumber, nullableoptional
bundle_buyer_price_pencenumber, nullableoptional
bundle_totalnumber, nullableoptional

How many boxes were put up.

bundle_remainingnumber, nullableoptional

How many boxes are left. Stock, never writable.

is_bundlebooleanoptional

True when this drop sells one box at one price.

blockersarray of stringrequired
blocker_codesarray of objectoptional
2 child fields
codeenumrequired
no_itemsno_timesno_closesno_quantityno_priceno_allergenstime_before_openstime_before_closestime_off_day
namesarray of stringrequired
making_started_atstring, nullableoptional
created_atstringrequired
updated_atstringrequired
curl -X POST "https://www.membber.com/api/v1/drops" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "name": "Example name",
    "opens_at": "<opens_at>",
    "collection_date": "<collection_date>"
  }'
Response, 200
{
  "drop": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "code": "EXAMPLE10",
    "shop_url": "https://example.com/image.jpg",
    "image_url": "https://example.com/image.jpg",
    "share_url": "https://example.com/image.jpg",
    "name": "Example name",
    "description": "Added at the front desk",
    "status": "draft",
    "state": "draft",
    "opens_at": "<opens_at>",
    "closes_at": "<closes_at>",
    "collection_date": "<collection_date>",
    "timezone": "Europe/London",
    "checkout_ttl_seconds": 1,
    "menu_visible_before_open": true,
    "show_stock_count": true,
    "max_per_customer": 1,
    "items": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "name": "Example name",
        "item_name": "<item_name>",
        "size_label": "<size_label>",
        "colour_label": "<colour_label>",
        "size_name": "<size_name>",
        "colour_name": "<colour_name>",
        "colour_photo_url": "https://example.com/image.jpg",
        "price_pence": 1500,
        "usual_price_pence": 1500,
        "drop_price_pence": 1500,
        "buyer_price_pence": 1500,
        "image_url": "https://example.com/image.jpg",
        "quantity": 1,
        "remaining": 1,
        "is_unlimited": true,
        "sold": 1,
        "max_per_order": 1,
        "per_bundle": -9007199254740991,
        "sold_out_at": "<sold_out_at>",
        "sort_order": 1,
        "allergens": [
          "<allergen>"
        ],
        "allergens_declared_none": true,
        "allergens_missing": true,
        "is_food": true
      }
    ],
    "windows": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "starts_at": "<starts_at>",
        "ends_at": "<ends_at>",
        "max_orders": 1,
        "orders_remaining": 1,
        "booked_count": 1,
        "booked_names": [
          "<booked_name>"
        ],
        "removable": true,
        "in_dispute": true
      }
    ],
    "moves_not_reached": {
      "not_told": -9007199254740991,
      "not_told_names": [
        "<not_told_name>"
      ]
    },
    "buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "cancelled_buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "orders_count": 1,
    "units_sold": 1,
    "units_total": 1,
    "has_unlimited": true,
    "gross_pence": 1500,
    "still_to_come_pence": 1500,
    "unpaid_orders_count": 1,
    "bundle_price_pence": 1500,
    "bundle_buyer_price_pence": 1500,
    "bundle_total": 1,
    "bundle_remaining": 1,
    "is_bundle": true,
    "blockers": [
      "<blocker>"
    ],
    "blocker_codes": [
      {
        "code": "no_items",
        "names": [
          "Example name"
        ]
      }
    ],
    "making_started_at": "<making_started_at>",
    "created_at": "<created_at>",
    "updated_at": "<updated_at>"
  }
}

Delete a drop that has never taken money.

Scoped to the calling store. Refused with a reason when the drop has paid orders or reviews (cancel those instead), or while somebody has its stock in a basket right now.

Parameters

dropIdstringpathrequired
drop_idstring · uuidqueryoptional

Supplied by the URL PATH.

Response, 200

deletedbooleanrequired
codestring, nullablerequired
namestring, nullablerequired
curl -X DELETE "https://www.membber.com/api/v1/drops/b11f386a-0000-4000-8000-d0c5000000b1" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678"
Response, 200
{
  "deleted": true,
  "code": "EXAMPLE10",
  "name": "Example name"
}

One drop, in full.

The drop with its items, windows, live counters and anything blocking it going live.

Parameters

dropIdstringpathrequired
store_idstring · uuidqueryrequired
drop_idstring · uuidqueryoptional

Response, 200

dropobjectrequired
40 child fields
idstringrequired
store_idstringrequired
codestringrequired

The short code that identifies this drop.

shop_urlstring, nullablerequired

The shop's permanent page, at its own address: https://{handle}.membber.com/

image_urlstring, nullablerequired
share_urlstringrequired

The link to this specific drop.

namestringrequired
descriptionstring, nullablerequired
statusenumrequired
draftscheduledcancelled
stateenumrequired

Computed at read time. Never sent by a client.

draftscheduledliveclosedcollectedcancelled
opens_atstringrequired
closes_atstring, nullablerequired

Null only while a draft has no closing time yet. A scheduled drop always has one.

collection_datestringrequired
timezonestringrequired
checkout_ttl_secondsnumberrequired
menu_visible_before_openbooleanrequired
show_stock_countbooleanrequired
max_per_customernumber, nullablerequired
itemsarray of objectrequired
26 child fields
idstringrequired
menu_item_idstringrequired
namestringrequired

Copied from the menu item at read time, never stored twice. On a line that sells a size or colour: the item’s name with them, "Hoodie, M, Black".

item_namestringoptional

Her item’s own name, on a line that sells one of its sizes or colours (`name` then reads "Hoodie, M, Black").

size_labelstringoptional

The size this line sells, as her item names it. Absent: no size.

colour_labelstringoptional

The colour this line sells, as her item names it. Absent: no colour.

size_namestringoptional

Her own word for this item’s sizes ("Flavour"), when she chose one. Absent: the word for size.

colour_namestringoptional

Her own word for this item’s colours ("Grind"), when she chose one. Absent: the word for colour.

colour_photo_urlstringoptional

The photo she chose for this colour, one of the item’s own photos. Absent: none chosen.

price_pencenumber, nullablerequired

What one sells at in this drop, before Membber's fee: the drop's own price when it has one, else the menu price. What checkout charges.

usual_price_pencenumber, nullableoptional

Her usual (menu) price for one.

drop_price_penceinteger, nullableoptional

This drop's own price for one, or null when it sells at the usual price.

buyer_price_pencenumber, nullablerequired
image_urlstring, nullablerequired
quantitynumberrequired

How many were put up. Meaningless when is_unlimited.

remainingnumberrequired

How many are still available. Never negative. Mirrors quantity when is_unlimited.

is_unlimitedbooleanrequired
soldnumberrequired
max_per_ordernumber, nullablerequired

null = no limit.

per_bundleinteger, nullableoptional

How many of this item go in one box of a one-price drop. Null means one.

sold_out_atstring, nullablerequired

Always null for an unlimited item.

sort_ordernumberrequired
allergensarray of stringrequired
allergens_declared_nonebooleanrequired
allergens_missingbooleanrequired
is_foodbooleanoptional

False for an item that is not food (clothes, flowers): no allergen answer is asked of it. True or absent: food.

windowsarray of objectrequired
9 child fields
idstringrequired

Send it back on the windows PUT to edit this window in place.

starts_atstringrequired
ends_atstringrequired
max_ordersnumber, nullablerequired

null = no cap on orders in this window.

orders_remainingnumber, nullablerequired
booked_countintegeroptional

People still to collect from this window. Moving it needs confirm_booked_changes; removing it is refused.

booked_namesarray of stringoptional

First names of the people still to collect, oldest order first.

removablebooleanoptional

False when anyone holds a place in this window, collected or not: removing it is refused.

in_disputebooleanoptional

True when the only orders holding this window are disputed ones nobody collected. Absent otherwise.

moves_not_reachedobjectoptional

Buyers still to collect whom a moved collection time could not reach. Absent when there are none.

2 child fields
not_toldintegerrequired

How many buyers could not be reached about a moved time.

not_told_namesarray of stringoptional

Their first names, oldest order first, three at most (the most the sentence names). Shorter than not_told when one gave no name or there are more than three; absent when none gave one.

buyersarray of objectoptional
21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

cancelled_buyersarray of objectoptional

Orders refunded in full, disputed by the buyer, released unpaid or cancelled with the drop, newest first. Listed only: never in buyers and never in any count or total.

21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

orders_countnumberrequired
units_soldnumberrequired
units_totalnumberrequired

Sum of the counted items only; an unlimited item adds nothing.

has_unlimitedbooleanrequired
gross_pencenumberrequired

TAKEN: money that has arrived and stayed, from every paid status (paid, ready, collected, part-refunded), net of recorded refunds. Card orders without the buyer fee; cash and transfer in full.

still_to_come_pencenumberrequired
unpaid_orders_countnumberrequired
bundle_price_pencenumber, nullableoptional
bundle_buyer_price_pencenumber, nullableoptional
bundle_totalnumber, nullableoptional

How many boxes were put up.

bundle_remainingnumber, nullableoptional

How many boxes are left. Stock, never writable.

is_bundlebooleanoptional

True when this drop sells one box at one price.

blockersarray of stringrequired
blocker_codesarray of objectoptional
2 child fields
codeenumrequired
no_itemsno_timesno_closesno_quantityno_priceno_allergenstime_before_openstime_before_closestime_off_day
namesarray of stringrequired
making_started_atstring, nullableoptional
created_atstringrequired
updated_atstringrequired
curl -G "https://www.membber.com/api/v1/drops/b11f386a-0000-4000-8000-d0c5000000b1" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066"
Response, 200
{
  "drop": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "code": "EXAMPLE10",
    "shop_url": "https://example.com/image.jpg",
    "image_url": "https://example.com/image.jpg",
    "share_url": "https://example.com/image.jpg",
    "name": "Example name",
    "description": "Added at the front desk",
    "status": "draft",
    "state": "draft",
    "opens_at": "<opens_at>",
    "closes_at": "<closes_at>",
    "collection_date": "<collection_date>",
    "timezone": "Europe/London",
    "checkout_ttl_seconds": 1,
    "menu_visible_before_open": true,
    "show_stock_count": true,
    "max_per_customer": 1,
    "items": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "name": "Example name",
        "item_name": "<item_name>",
        "size_label": "<size_label>",
        "colour_label": "<colour_label>",
        "size_name": "<size_name>",
        "colour_name": "<colour_name>",
        "colour_photo_url": "https://example.com/image.jpg",
        "price_pence": 1500,
        "usual_price_pence": 1500,
        "drop_price_pence": 1500,
        "buyer_price_pence": 1500,
        "image_url": "https://example.com/image.jpg",
        "quantity": 1,
        "remaining": 1,
        "is_unlimited": true,
        "sold": 1,
        "max_per_order": 1,
        "per_bundle": -9007199254740991,
        "sold_out_at": "<sold_out_at>",
        "sort_order": 1,
        "allergens": [
          "<allergen>"
        ],
        "allergens_declared_none": true,
        "allergens_missing": true,
        "is_food": true
      }
    ],
    "windows": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "starts_at": "<starts_at>",
        "ends_at": "<ends_at>",
        "max_orders": 1,
        "orders_remaining": 1,
        "booked_count": 1,
        "booked_names": [
          "<booked_name>"
        ],
        "removable": true,
        "in_dispute": true
      }
    ],
    "moves_not_reached": {
      "not_told": -9007199254740991,
      "not_told_names": [
        "<not_told_name>"
      ]
    },
    "buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "cancelled_buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "orders_count": 1,
    "units_sold": 1,
    "units_total": 1,
    "has_unlimited": true,
    "gross_pence": 1500,
    "still_to_come_pence": 1500,
    "unpaid_orders_count": 1,
    "bundle_price_pence": 1500,
    "bundle_buyer_price_pence": 1500,
    "bundle_total": 1,
    "bundle_remaining": 1,
    "is_bundle": true,
    "blockers": [
      "<blocker>"
    ],
    "blocker_codes": [
      {
        "code": "no_items",
        "names": [
          "Example name"
        ]
      }
    ],
    "making_started_at": "<making_started_at>",
    "created_at": "<created_at>",
    "updated_at": "<updated_at>"
  }
}

Change a drop.

Edits a drop, including one that is currently selling: adding stock, reordering items, adding a window or closing early are all expected mid-drop. Moving `status` from draft to scheduled is what makes it go live at its opening minute; that is refused while anything is still missing (an item's allergens or price, a collection window, the closing time) and the refusal says what. The check reads the drop as this PATCH leaves it, so the closing time may arrive in the same request as the status. A new `collection_date` moves every collection window to that day in the same write (clock times kept); if people booked one, it is refused with 409 BOOKED_WINDOWS_AFFECTED until sent with confirm_booked_changes.

Parameters

dropIdstringpathrequired

Request body

store_idstring · uuidrequired
drop_idstring · uuidoptional
statusenumoptional
draftscheduled
namestringoptional
descriptionstring, nullableoptional
opens_atstringoptional

ISO instant. The drop opens itself at this minute.

closes_atstring, nullableoptional

ISO instant. Null is allowed on a DRAFT (nothing is on sale yet, so nothing closes), but a drop cannot be scheduled without a closing time. It may be sent in the same PATCH as status=scheduled.

collection_datestringoptional

YYYY-MM-DD in the store timezone.

bundle_price_penceinteger, nullableoptional

What the whole box costs, in pence. Null means this is not a box drop.

bundle_buyer_price_pencenumber, nullableoptional
bundle_totalinteger, nullableoptional

How many boxes exist. Null means this is not a box drop.

timezonestringoptional
checkout_ttl_secondsanyoptional

Basket hold, in seconds. Defaults to 300 (5 minutes).

menu_visible_before_openbooleanoptional
show_stock_countbooleanoptional
max_per_customerinteger, nullableoptional

null = no limit.

image_urlstring, nullableoptional
confirm_booked_changesbooleanoptional

True once the seller has agreed to move collection times people booked. Without it such a change is refused with 409 BOOKED_WINDOWS_AFFECTED naming them. Each affected buyer is told once.

header_auto_advancebooleanoptional

Response, 200

dropobjectrequired
40 child fields
idstringrequired
store_idstringrequired
codestringrequired

The short code that identifies this drop.

shop_urlstring, nullablerequired

The shop's permanent page, at its own address: https://{handle}.membber.com/

image_urlstring, nullablerequired
share_urlstringrequired

The link to this specific drop.

namestringrequired
descriptionstring, nullablerequired
statusenumrequired
draftscheduledcancelled
stateenumrequired

Computed at read time. Never sent by a client.

draftscheduledliveclosedcollectedcancelled
opens_atstringrequired
closes_atstring, nullablerequired

Null only while a draft has no closing time yet. A scheduled drop always has one.

collection_datestringrequired
timezonestringrequired
checkout_ttl_secondsnumberrequired
menu_visible_before_openbooleanrequired
show_stock_countbooleanrequired
max_per_customernumber, nullablerequired
itemsarray of objectrequired
26 child fields
idstringrequired
menu_item_idstringrequired
namestringrequired

Copied from the menu item at read time, never stored twice. On a line that sells a size or colour: the item’s name with them, "Hoodie, M, Black".

item_namestringoptional

Her item’s own name, on a line that sells one of its sizes or colours (`name` then reads "Hoodie, M, Black").

size_labelstringoptional

The size this line sells, as her item names it. Absent: no size.

colour_labelstringoptional

The colour this line sells, as her item names it. Absent: no colour.

size_namestringoptional

Her own word for this item’s sizes ("Flavour"), when she chose one. Absent: the word for size.

colour_namestringoptional

Her own word for this item’s colours ("Grind"), when she chose one. Absent: the word for colour.

colour_photo_urlstringoptional

The photo she chose for this colour, one of the item’s own photos. Absent: none chosen.

price_pencenumber, nullablerequired

What one sells at in this drop, before Membber's fee: the drop's own price when it has one, else the menu price. What checkout charges.

usual_price_pencenumber, nullableoptional

Her usual (menu) price for one.

drop_price_penceinteger, nullableoptional

This drop's own price for one, or null when it sells at the usual price.

buyer_price_pencenumber, nullablerequired
image_urlstring, nullablerequired
quantitynumberrequired

How many were put up. Meaningless when is_unlimited.

remainingnumberrequired

How many are still available. Never negative. Mirrors quantity when is_unlimited.

is_unlimitedbooleanrequired
soldnumberrequired
max_per_ordernumber, nullablerequired

null = no limit.

per_bundleinteger, nullableoptional

How many of this item go in one box of a one-price drop. Null means one.

sold_out_atstring, nullablerequired

Always null for an unlimited item.

sort_ordernumberrequired
allergensarray of stringrequired
allergens_declared_nonebooleanrequired
allergens_missingbooleanrequired
is_foodbooleanoptional

False for an item that is not food (clothes, flowers): no allergen answer is asked of it. True or absent: food.

windowsarray of objectrequired
9 child fields
idstringrequired

Send it back on the windows PUT to edit this window in place.

starts_atstringrequired
ends_atstringrequired
max_ordersnumber, nullablerequired

null = no cap on orders in this window.

orders_remainingnumber, nullablerequired
booked_countintegeroptional

People still to collect from this window. Moving it needs confirm_booked_changes; removing it is refused.

booked_namesarray of stringoptional

First names of the people still to collect, oldest order first.

removablebooleanoptional

False when anyone holds a place in this window, collected or not: removing it is refused.

in_disputebooleanoptional

True when the only orders holding this window are disputed ones nobody collected. Absent otherwise.

moves_not_reachedobjectoptional

Buyers still to collect whom a moved collection time could not reach. Absent when there are none.

2 child fields
not_toldintegerrequired

How many buyers could not be reached about a moved time.

not_told_namesarray of stringoptional

Their first names, oldest order first, three at most (the most the sentence names). Shorter than not_told when one gave no name or there are more than three; absent when none gave one.

buyersarray of objectoptional
21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

cancelled_buyersarray of objectoptional

Orders refunded in full, disputed by the buyer, released unpaid or cancelled with the drop, newest first. Listed only: never in buyers and never in any count or total.

21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

orders_countnumberrequired
units_soldnumberrequired
units_totalnumberrequired

Sum of the counted items only; an unlimited item adds nothing.

has_unlimitedbooleanrequired
gross_pencenumberrequired

TAKEN: money that has arrived and stayed, from every paid status (paid, ready, collected, part-refunded), net of recorded refunds. Card orders without the buyer fee; cash and transfer in full.

still_to_come_pencenumberrequired
unpaid_orders_countnumberrequired
bundle_price_pencenumber, nullableoptional
bundle_buyer_price_pencenumber, nullableoptional
bundle_totalnumber, nullableoptional

How many boxes were put up.

bundle_remainingnumber, nullableoptional

How many boxes are left. Stock, never writable.

is_bundlebooleanoptional

True when this drop sells one box at one price.

blockersarray of stringrequired
blocker_codesarray of objectoptional
2 child fields
codeenumrequired
no_itemsno_timesno_closesno_quantityno_priceno_allergenstime_before_openstime_before_closestime_off_day
namesarray of stringrequired
making_started_atstring, nullableoptional
created_atstringrequired
updated_atstringrequired
moved_bookingsobjectoptional

Who a move of booked collection times reached.

4 child fields
ordersintegerrequired

Orders whose collection time moved with their window.

toldintegerrequired

Of those, buyers told the new time by text or email.

not_toldintegerrequired

Buyers who could not be told (no mobile or email that worked). The seller should reach them. A buyer whose message is still waiting to be sent is in neither count.

not_told_namesarray of stringoptional

First names of the buyers who could not be told, oldest order first, as given on their orders at this shop (the names `windows[].booked_names` gave before the move), three at most: the most the sentence names, which from four says the first two and counts the rest from `not_told`. A buyer who gave no name is not in it, so it can be shorter than `not_told`. Absent when none of them gave one: say the count.

curl -X PATCH "https://www.membber.com/api/v1/drops/b11f386a-0000-4000-8000-d0c5000000b1" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066"
  }'
Response, 200
{
  "drop": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "code": "EXAMPLE10",
    "shop_url": "https://example.com/image.jpg",
    "image_url": "https://example.com/image.jpg",
    "share_url": "https://example.com/image.jpg",
    "name": "Example name",
    "description": "Added at the front desk",
    "status": "draft",
    "state": "draft",
    "opens_at": "<opens_at>",
    "closes_at": "<closes_at>",
    "collection_date": "<collection_date>",
    "timezone": "Europe/London",
    "checkout_ttl_seconds": 1,
    "menu_visible_before_open": true,
    "show_stock_count": true,
    "max_per_customer": 1,
    "items": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "name": "Example name",
        "item_name": "<item_name>",
        "size_label": "<size_label>",
        "colour_label": "<colour_label>",
        "size_name": "<size_name>",
        "colour_name": "<colour_name>",
        "colour_photo_url": "https://example.com/image.jpg",
        "price_pence": 1500,
        "usual_price_pence": 1500,
        "drop_price_pence": 1500,
        "buyer_price_pence": 1500,
        "image_url": "https://example.com/image.jpg",
        "quantity": 1,
        "remaining": 1,
        "is_unlimited": true,
        "sold": 1,
        "max_per_order": 1,
        "per_bundle": -9007199254740991,
        "sold_out_at": "<sold_out_at>",
        "sort_order": 1,
        "allergens": [
          "<allergen>"
        ],
        "allergens_declared_none": true,
        "allergens_missing": true,
        "is_food": true
      }
    ],
    "windows": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "starts_at": "<starts_at>",
        "ends_at": "<ends_at>",
        "max_orders": 1,
        "orders_remaining": 1,
        "booked_count": 1,
        "booked_names": [
          "<booked_name>"
        ],
        "removable": true,
        "in_dispute": true
      }
    ],
    "moves_not_reached": {
      "not_told": -9007199254740991,
      "not_told_names": [
        "<not_told_name>"
      ]
    },
    "buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "cancelled_buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "orders_count": 1,
    "units_sold": 1,
    "units_total": 1,
    "has_unlimited": true,
    "gross_pence": 1500,
    "still_to_come_pence": 1500,
    "unpaid_orders_count": 1,
    "bundle_price_pence": 1500,
    "bundle_buyer_price_pence": 1500,
    "bundle_total": 1,
    "bundle_remaining": 1,
    "is_bundle": true,
    "blockers": [
      "<blocker>"
    ],
    "blocker_codes": [
      {
        "code": "no_items",
        "names": [
          "Example name"
        ]
      }
    ],
    "making_started_at": "<making_started_at>",
    "created_at": "<created_at>",
    "updated_at": "<updated_at>"
  },
  "moved_bookings": {
    "orders": -9007199254740991,
    "told": -9007199254740991,
    "not_told": -9007199254740991,
    "not_told_names": [
      "<not_told_name>"
    ]
  }
}

Call a drop off and refund everyone.

Cancels the drop. Any paid order is refunded and every affected person is told once, however many orders they hold. A drop with paid orders requires `confirm_refunds: true`, so nobody cancels fifty people without being shown the number first. An order already collected, or marked not collected, is neither refunded nor told. `preview: true` answers what it would do and changes nothing. Requires the refund permission.

Parameters

dropIdstringpathrequired

Request body

store_idstring · uuidrequired
drop_idstring · uuidoptional
reasonstring, nullableoptional
confirm_refundsbooleanoptional
previewbooleanoptional

True: only say what calling the drop off would do (the same numbers and sentence), and change nothing, whatever else is sent.

Response, 200

dropobjectrequired
40 child fields
idstringrequired
store_idstringrequired
codestringrequired

The short code that identifies this drop.

shop_urlstring, nullablerequired

The shop's permanent page, at its own address: https://{handle}.membber.com/

image_urlstring, nullablerequired
share_urlstringrequired

The link to this specific drop.

namestringrequired
descriptionstring, nullablerequired
statusenumrequired
draftscheduledcancelled
stateenumrequired

Computed at read time. Never sent by a client.

draftscheduledliveclosedcollectedcancelled
opens_atstringrequired
closes_atstring, nullablerequired

Null only while a draft has no closing time yet. A scheduled drop always has one.

collection_datestringrequired
timezonestringrequired
checkout_ttl_secondsnumberrequired
menu_visible_before_openbooleanrequired
show_stock_countbooleanrequired
max_per_customernumber, nullablerequired
itemsarray of objectrequired
26 child fields
idstringrequired
menu_item_idstringrequired
namestringrequired

Copied from the menu item at read time, never stored twice. On a line that sells a size or colour: the item’s name with them, "Hoodie, M, Black".

item_namestringoptional

Her item’s own name, on a line that sells one of its sizes or colours (`name` then reads "Hoodie, M, Black").

size_labelstringoptional

The size this line sells, as her item names it. Absent: no size.

colour_labelstringoptional

The colour this line sells, as her item names it. Absent: no colour.

size_namestringoptional

Her own word for this item’s sizes ("Flavour"), when she chose one. Absent: the word for size.

colour_namestringoptional

Her own word for this item’s colours ("Grind"), when she chose one. Absent: the word for colour.

colour_photo_urlstringoptional

The photo she chose for this colour, one of the item’s own photos. Absent: none chosen.

price_pencenumber, nullablerequired

What one sells at in this drop, before Membber's fee: the drop's own price when it has one, else the menu price. What checkout charges.

usual_price_pencenumber, nullableoptional

Her usual (menu) price for one.

drop_price_penceinteger, nullableoptional

This drop's own price for one, or null when it sells at the usual price.

buyer_price_pencenumber, nullablerequired
image_urlstring, nullablerequired
quantitynumberrequired

How many were put up. Meaningless when is_unlimited.

remainingnumberrequired

How many are still available. Never negative. Mirrors quantity when is_unlimited.

is_unlimitedbooleanrequired
soldnumberrequired
max_per_ordernumber, nullablerequired

null = no limit.

per_bundleinteger, nullableoptional

How many of this item go in one box of a one-price drop. Null means one.

sold_out_atstring, nullablerequired

Always null for an unlimited item.

sort_ordernumberrequired
allergensarray of stringrequired
allergens_declared_nonebooleanrequired
allergens_missingbooleanrequired
is_foodbooleanoptional

False for an item that is not food (clothes, flowers): no allergen answer is asked of it. True or absent: food.

windowsarray of objectrequired
9 child fields
idstringrequired

Send it back on the windows PUT to edit this window in place.

starts_atstringrequired
ends_atstringrequired
max_ordersnumber, nullablerequired

null = no cap on orders in this window.

orders_remainingnumber, nullablerequired
booked_countintegeroptional

People still to collect from this window. Moving it needs confirm_booked_changes; removing it is refused.

booked_namesarray of stringoptional

First names of the people still to collect, oldest order first.

removablebooleanoptional

False when anyone holds a place in this window, collected or not: removing it is refused.

in_disputebooleanoptional

True when the only orders holding this window are disputed ones nobody collected. Absent otherwise.

moves_not_reachedobjectoptional

Buyers still to collect whom a moved collection time could not reach. Absent when there are none.

2 child fields
not_toldintegerrequired

How many buyers could not be reached about a moved time.

not_told_namesarray of stringoptional

Their first names, oldest order first, three at most (the most the sentence names). Shorter than not_told when one gave no name or there are more than three; absent when none gave one.

buyersarray of objectoptional
21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

cancelled_buyersarray of objectoptional

Orders refunded in full, disputed by the buyer, released unpaid or cancelled with the drop, newest first. Listed only: never in buyers and never in any count or total.

21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

orders_countnumberrequired
units_soldnumberrequired
units_totalnumberrequired

Sum of the counted items only; an unlimited item adds nothing.

has_unlimitedbooleanrequired
gross_pencenumberrequired

TAKEN: money that has arrived and stayed, from every paid status (paid, ready, collected, part-refunded), net of recorded refunds. Card orders without the buyer fee; cash and transfer in full.

still_to_come_pencenumberrequired
unpaid_orders_countnumberrequired
bundle_price_pencenumber, nullableoptional
bundle_buyer_price_pencenumber, nullableoptional
bundle_totalnumber, nullableoptional

How many boxes were put up.

bundle_remainingnumber, nullableoptional

How many boxes are left. Stock, never writable.

is_bundlebooleanoptional

True when this drop sells one box at one price.

blockersarray of stringrequired
blocker_codesarray of objectoptional
2 child fields
codeenumrequired
no_itemsno_timesno_closesno_quantityno_priceno_allergenstime_before_openstime_before_closestime_off_day
namesarray of stringrequired
making_started_atstring, nullableoptional
created_atstringrequired
updated_atstringrequired
orders_to_refundnumberrequired

How many orders will be refunded.

people_to_tellnumberrequired

How many PEOPLE, which is fewer if anyone ordered twice.

refund_pencenumberrequired
previewbooleanoptional

True on the answer to a preview: nothing was changed.

messagestringoptional

On a preview: who is refunded and who is told, in the sentence the merchant confirms. Empty when nobody is owed anything or told.

curl -X POST "https://www.membber.com/api/v1/drops/b11f386a-0000-4000-8000-d0c5000000b1/cancel" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066"
  }'
Response, 200
{
  "drop": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "code": "EXAMPLE10",
    "shop_url": "https://example.com/image.jpg",
    "image_url": "https://example.com/image.jpg",
    "share_url": "https://example.com/image.jpg",
    "name": "Example name",
    "description": "Added at the front desk",
    "status": "draft",
    "state": "draft",
    "opens_at": "<opens_at>",
    "closes_at": "<closes_at>",
    "collection_date": "<collection_date>",
    "timezone": "Europe/London",
    "checkout_ttl_seconds": 1,
    "menu_visible_before_open": true,
    "show_stock_count": true,
    "max_per_customer": 1,
    "items": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "name": "Example name",
        "item_name": "<item_name>",
        "size_label": "<size_label>",
        "colour_label": "<colour_label>",
        "size_name": "<size_name>",
        "colour_name": "<colour_name>",
        "colour_photo_url": "https://example.com/image.jpg",
        "price_pence": 1500,
        "usual_price_pence": 1500,
        "drop_price_pence": 1500,
        "buyer_price_pence": 1500,
        "image_url": "https://example.com/image.jpg",
        "quantity": 1,
        "remaining": 1,
        "is_unlimited": true,
        "sold": 1,
        "max_per_order": 1,
        "per_bundle": -9007199254740991,
        "sold_out_at": "<sold_out_at>",
        "sort_order": 1,
        "allergens": [
          "<allergen>"
        ],
        "allergens_declared_none": true,
        "allergens_missing": true,
        "is_food": true
      }
    ],
    "windows": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "starts_at": "<starts_at>",
        "ends_at": "<ends_at>",
        "max_orders": 1,
        "orders_remaining": 1,
        "booked_count": 1,
        "booked_names": [
          "<booked_name>"
        ],
        "removable": true,
        "in_dispute": true
      }
    ],
    "moves_not_reached": {
      "not_told": -9007199254740991,
      "not_told_names": [
        "<not_told_name>"
      ]
    },
    "buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "cancelled_buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "orders_count": 1,
    "units_sold": 1,
    "units_total": 1,
    "has_unlimited": true,
    "gross_pence": 1500,
    "still_to_come_pence": 1500,
    "unpaid_orders_count": 1,
    "bundle_price_pence": 1500,
    "bundle_buyer_price_pence": 1500,
    "bundle_total": 1,
    "bundle_remaining": 1,
    "is_bundle": true,
    "blockers": [
      "<blocker>"
    ],
    "blocker_codes": [
      {
        "code": "no_items",
        "names": [
          "Example name"
        ]
      }
    ],
    "making_started_at": "<making_started_at>",
    "created_at": "<created_at>",
    "updated_at": "<updated_at>"
  },
  "orders_to_refund": 1,
  "people_to_tell": 1,
  "refund_pence": 1500,
  "preview": true,
  "message": "Added at the front desk"
}

Run that drop again.

Copies a drop's name, description, photo, items and quantities, collection windows and settings onto a new date, as a DRAFT. With no dates supplied it moves forward in whole weeks from the original, keeping the weekday and the time of day. Orders, sales and announcements are never copied, and the original keeps its link.

Parameters

dropIdstringpathrequired

Request body

store_idstring · uuidrequired
drop_idstring · uuidoptional
client_tokenstringoptional

Send the same token on a retry to get the SAME drop back instead of a duplicate.

namestringoptional
opens_atstringoptional
closes_atstring, nullableoptional

Defaults to the source's closing time, moved with the date. Null leaves the copy without one: it is a draft, and cannot be scheduled until a closing time is set.

collection_datestringoptional

Response, 200

dropobjectrequired
40 child fields
idstringrequired
store_idstringrequired
codestringrequired

The short code that identifies this drop.

shop_urlstring, nullablerequired

The shop's permanent page, at its own address: https://{handle}.membber.com/

image_urlstring, nullablerequired
share_urlstringrequired

The link to this specific drop.

namestringrequired
descriptionstring, nullablerequired
statusenumrequired
draftscheduledcancelled
stateenumrequired

Computed at read time. Never sent by a client.

draftscheduledliveclosedcollectedcancelled
opens_atstringrequired
closes_atstring, nullablerequired

Null only while a draft has no closing time yet. A scheduled drop always has one.

collection_datestringrequired
timezonestringrequired
checkout_ttl_secondsnumberrequired
menu_visible_before_openbooleanrequired
show_stock_countbooleanrequired
max_per_customernumber, nullablerequired
itemsarray of objectrequired
26 child fields
idstringrequired
menu_item_idstringrequired
namestringrequired

Copied from the menu item at read time, never stored twice. On a line that sells a size or colour: the item’s name with them, "Hoodie, M, Black".

item_namestringoptional

Her item’s own name, on a line that sells one of its sizes or colours (`name` then reads "Hoodie, M, Black").

size_labelstringoptional

The size this line sells, as her item names it. Absent: no size.

colour_labelstringoptional

The colour this line sells, as her item names it. Absent: no colour.

size_namestringoptional

Her own word for this item’s sizes ("Flavour"), when she chose one. Absent: the word for size.

colour_namestringoptional

Her own word for this item’s colours ("Grind"), when she chose one. Absent: the word for colour.

colour_photo_urlstringoptional

The photo she chose for this colour, one of the item’s own photos. Absent: none chosen.

price_pencenumber, nullablerequired

What one sells at in this drop, before Membber's fee: the drop's own price when it has one, else the menu price. What checkout charges.

usual_price_pencenumber, nullableoptional

Her usual (menu) price for one.

drop_price_penceinteger, nullableoptional

This drop's own price for one, or null when it sells at the usual price.

buyer_price_pencenumber, nullablerequired
image_urlstring, nullablerequired
quantitynumberrequired

How many were put up. Meaningless when is_unlimited.

remainingnumberrequired

How many are still available. Never negative. Mirrors quantity when is_unlimited.

is_unlimitedbooleanrequired
soldnumberrequired
max_per_ordernumber, nullablerequired

null = no limit.

per_bundleinteger, nullableoptional

How many of this item go in one box of a one-price drop. Null means one.

sold_out_atstring, nullablerequired

Always null for an unlimited item.

sort_ordernumberrequired
allergensarray of stringrequired
allergens_declared_nonebooleanrequired
allergens_missingbooleanrequired
is_foodbooleanoptional

False for an item that is not food (clothes, flowers): no allergen answer is asked of it. True or absent: food.

windowsarray of objectrequired
9 child fields
idstringrequired

Send it back on the windows PUT to edit this window in place.

starts_atstringrequired
ends_atstringrequired
max_ordersnumber, nullablerequired

null = no cap on orders in this window.

orders_remainingnumber, nullablerequired
booked_countintegeroptional

People still to collect from this window. Moving it needs confirm_booked_changes; removing it is refused.

booked_namesarray of stringoptional

First names of the people still to collect, oldest order first.

removablebooleanoptional

False when anyone holds a place in this window, collected or not: removing it is refused.

in_disputebooleanoptional

True when the only orders holding this window are disputed ones nobody collected. Absent otherwise.

moves_not_reachedobjectoptional

Buyers still to collect whom a moved collection time could not reach. Absent when there are none.

2 child fields
not_toldintegerrequired

How many buyers could not be reached about a moved time.

not_told_namesarray of stringoptional

Their first names, oldest order first, three at most (the most the sentence names). Shorter than not_told when one gave no name or there are more than three; absent when none gave one.

buyersarray of objectoptional
21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

cancelled_buyersarray of objectoptional

Orders refunded in full, disputed by the buyer, released unpaid or cancelled with the drop, newest first. Listed only: never in buyers and never in any count or total.

21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

orders_countnumberrequired
units_soldnumberrequired
units_totalnumberrequired

Sum of the counted items only; an unlimited item adds nothing.

has_unlimitedbooleanrequired
gross_pencenumberrequired

TAKEN: money that has arrived and stayed, from every paid status (paid, ready, collected, part-refunded), net of recorded refunds. Card orders without the buyer fee; cash and transfer in full.

still_to_come_pencenumberrequired
unpaid_orders_countnumberrequired
bundle_price_pencenumber, nullableoptional
bundle_buyer_price_pencenumber, nullableoptional
bundle_totalnumber, nullableoptional

How many boxes were put up.

bundle_remainingnumber, nullableoptional

How many boxes are left. Stock, never writable.

is_bundlebooleanoptional

True when this drop sells one box at one price.

blockersarray of stringrequired
blocker_codesarray of objectoptional
2 child fields
codeenumrequired
no_itemsno_timesno_closesno_quantityno_priceno_allergenstime_before_openstime_before_closestime_off_day
namesarray of stringrequired
making_started_atstring, nullableoptional
created_atstringrequired
updated_atstringrequired
curl -X POST "https://www.membber.com/api/v1/drops/b11f386a-0000-4000-8000-d0c5000000b1/duplicate" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066"
  }'
Response, 200
{
  "drop": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "code": "EXAMPLE10",
    "shop_url": "https://example.com/image.jpg",
    "image_url": "https://example.com/image.jpg",
    "share_url": "https://example.com/image.jpg",
    "name": "Example name",
    "description": "Added at the front desk",
    "status": "draft",
    "state": "draft",
    "opens_at": "<opens_at>",
    "closes_at": "<closes_at>",
    "collection_date": "<collection_date>",
    "timezone": "Europe/London",
    "checkout_ttl_seconds": 1,
    "menu_visible_before_open": true,
    "show_stock_count": true,
    "max_per_customer": 1,
    "items": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "name": "Example name",
        "item_name": "<item_name>",
        "size_label": "<size_label>",
        "colour_label": "<colour_label>",
        "size_name": "<size_name>",
        "colour_name": "<colour_name>",
        "colour_photo_url": "https://example.com/image.jpg",
        "price_pence": 1500,
        "usual_price_pence": 1500,
        "drop_price_pence": 1500,
        "buyer_price_pence": 1500,
        "image_url": "https://example.com/image.jpg",
        "quantity": 1,
        "remaining": 1,
        "is_unlimited": true,
        "sold": 1,
        "max_per_order": 1,
        "per_bundle": -9007199254740991,
        "sold_out_at": "<sold_out_at>",
        "sort_order": 1,
        "allergens": [
          "<allergen>"
        ],
        "allergens_declared_none": true,
        "allergens_missing": true,
        "is_food": true
      }
    ],
    "windows": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "starts_at": "<starts_at>",
        "ends_at": "<ends_at>",
        "max_orders": 1,
        "orders_remaining": 1,
        "booked_count": 1,
        "booked_names": [
          "<booked_name>"
        ],
        "removable": true,
        "in_dispute": true
      }
    ],
    "moves_not_reached": {
      "not_told": -9007199254740991,
      "not_told_names": [
        "<not_told_name>"
      ]
    },
    "buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "cancelled_buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "orders_count": 1,
    "units_sold": 1,
    "units_total": 1,
    "has_unlimited": true,
    "gross_pence": 1500,
    "still_to_come_pence": 1500,
    "unpaid_orders_count": 1,
    "bundle_price_pence": 1500,
    "bundle_buyer_price_pence": 1500,
    "bundle_total": 1,
    "bundle_remaining": 1,
    "is_bundle": true,
    "blockers": [
      "<blocker>"
    ],
    "blocker_codes": [
      {
        "code": "no_items",
        "names": [
          "Example name"
        ]
      }
    ],
    "making_started_at": "<making_started_at>",
    "created_at": "<created_at>",
    "updated_at": "<updated_at>"
  }
}

Set what is in the drop.

Replaces the drop menu with the given items, chosen from the store menu that already exists. Raising a quantity mid-drop puts stock back on sale immediately. Lowering it can never go below what is already sold, and the refusal says how many that is. An item omitted here is removed from the drop, which is refused if any of it has sold. In a one-price box drop, per_bundle says how many of an item go in one box; an item already in the drop keeps that count once the box is selling or any of it has sold. An item with sizes or colours sells as one line per pair (size_label, colour_label), each with its own quantity and drop_price_pence, and one max_per_order for all of its lines; a box has none.

Parameters

dropIdstringpathrequired

Request body

store_idstring · uuidrequired
drop_idstring · uuidoptional
itemsarray of objectrequired
9 child fields
menu_item_idstring · uuidrequired
size_labelstringoptional

The size this line sells, as her item names it ("" or absent: none). One line per size and colour.

colour_labelstringoptional

The colour this line sells, as her item names it ("" or absent: none). One line per size and colour.

quantityintegerrequired
is_unlimitedbooleanoptional
max_per_orderinteger, nullableoptional
sort_orderintegeroptional
drop_price_penceinteger, nullableoptional

This drop's own price for one, in pence. Leave it out to keep the saved price; null for the usual price.

per_bundleinteger, nullableoptional

How many of this item go in one box of a one-price drop. Leave it out to keep what is saved; null for one.

Response, 200

dropobjectrequired
40 child fields
idstringrequired
store_idstringrequired
codestringrequired

The short code that identifies this drop.

shop_urlstring, nullablerequired

The shop's permanent page, at its own address: https://{handle}.membber.com/

image_urlstring, nullablerequired
share_urlstringrequired

The link to this specific drop.

namestringrequired
descriptionstring, nullablerequired
statusenumrequired
draftscheduledcancelled
stateenumrequired

Computed at read time. Never sent by a client.

draftscheduledliveclosedcollectedcancelled
opens_atstringrequired
closes_atstring, nullablerequired

Null only while a draft has no closing time yet. A scheduled drop always has one.

collection_datestringrequired
timezonestringrequired
checkout_ttl_secondsnumberrequired
menu_visible_before_openbooleanrequired
show_stock_countbooleanrequired
max_per_customernumber, nullablerequired
itemsarray of objectrequired
26 child fields
idstringrequired
menu_item_idstringrequired
namestringrequired

Copied from the menu item at read time, never stored twice. On a line that sells a size or colour: the item’s name with them, "Hoodie, M, Black".

item_namestringoptional

Her item’s own name, on a line that sells one of its sizes or colours (`name` then reads "Hoodie, M, Black").

size_labelstringoptional

The size this line sells, as her item names it. Absent: no size.

colour_labelstringoptional

The colour this line sells, as her item names it. Absent: no colour.

size_namestringoptional

Her own word for this item’s sizes ("Flavour"), when she chose one. Absent: the word for size.

colour_namestringoptional

Her own word for this item’s colours ("Grind"), when she chose one. Absent: the word for colour.

colour_photo_urlstringoptional

The photo she chose for this colour, one of the item’s own photos. Absent: none chosen.

price_pencenumber, nullablerequired

What one sells at in this drop, before Membber's fee: the drop's own price when it has one, else the menu price. What checkout charges.

usual_price_pencenumber, nullableoptional

Her usual (menu) price for one.

drop_price_penceinteger, nullableoptional

This drop's own price for one, or null when it sells at the usual price.

buyer_price_pencenumber, nullablerequired
image_urlstring, nullablerequired
quantitynumberrequired

How many were put up. Meaningless when is_unlimited.

remainingnumberrequired

How many are still available. Never negative. Mirrors quantity when is_unlimited.

is_unlimitedbooleanrequired
soldnumberrequired
max_per_ordernumber, nullablerequired

null = no limit.

per_bundleinteger, nullableoptional

How many of this item go in one box of a one-price drop. Null means one.

sold_out_atstring, nullablerequired

Always null for an unlimited item.

sort_ordernumberrequired
allergensarray of stringrequired
allergens_declared_nonebooleanrequired
allergens_missingbooleanrequired
is_foodbooleanoptional

False for an item that is not food (clothes, flowers): no allergen answer is asked of it. True or absent: food.

windowsarray of objectrequired
9 child fields
idstringrequired

Send it back on the windows PUT to edit this window in place.

starts_atstringrequired
ends_atstringrequired
max_ordersnumber, nullablerequired

null = no cap on orders in this window.

orders_remainingnumber, nullablerequired
booked_countintegeroptional

People still to collect from this window. Moving it needs confirm_booked_changes; removing it is refused.

booked_namesarray of stringoptional

First names of the people still to collect, oldest order first.

removablebooleanoptional

False when anyone holds a place in this window, collected or not: removing it is refused.

in_disputebooleanoptional

True when the only orders holding this window are disputed ones nobody collected. Absent otherwise.

moves_not_reachedobjectoptional

Buyers still to collect whom a moved collection time could not reach. Absent when there are none.

2 child fields
not_toldintegerrequired

How many buyers could not be reached about a moved time.

not_told_namesarray of stringoptional

Their first names, oldest order first, three at most (the most the sentence names). Shorter than not_told when one gave no name or there are more than three; absent when none gave one.

buyersarray of objectoptional
21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

cancelled_buyersarray of objectoptional

Orders refunded in full, disputed by the buyer, released unpaid or cancelled with the drop, newest first. Listed only: never in buyers and never in any count or total.

21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

orders_countnumberrequired
units_soldnumberrequired
units_totalnumberrequired

Sum of the counted items only; an unlimited item adds nothing.

has_unlimitedbooleanrequired
gross_pencenumberrequired

TAKEN: money that has arrived and stayed, from every paid status (paid, ready, collected, part-refunded), net of recorded refunds. Card orders without the buyer fee; cash and transfer in full.

still_to_come_pencenumberrequired
unpaid_orders_countnumberrequired
bundle_price_pencenumber, nullableoptional
bundle_buyer_price_pencenumber, nullableoptional
bundle_totalnumber, nullableoptional

How many boxes were put up.

bundle_remainingnumber, nullableoptional

How many boxes are left. Stock, never writable.

is_bundlebooleanoptional

True when this drop sells one box at one price.

blockersarray of stringrequired
blocker_codesarray of objectoptional
2 child fields
codeenumrequired
no_itemsno_timesno_closesno_quantityno_priceno_allergenstime_before_openstime_before_closestime_off_day
namesarray of stringrequired
making_started_atstring, nullableoptional
created_atstringrequired
updated_atstringrequired
curl -X PUT "https://www.membber.com/api/v1/drops/b11f386a-0000-4000-8000-d0c5000000b1/items" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "items": [
      {
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "quantity": 1
      }
    ]
  }'
Response, 200
{
  "drop": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "code": "EXAMPLE10",
    "shop_url": "https://example.com/image.jpg",
    "image_url": "https://example.com/image.jpg",
    "share_url": "https://example.com/image.jpg",
    "name": "Example name",
    "description": "Added at the front desk",
    "status": "draft",
    "state": "draft",
    "opens_at": "<opens_at>",
    "closes_at": "<closes_at>",
    "collection_date": "<collection_date>",
    "timezone": "Europe/London",
    "checkout_ttl_seconds": 1,
    "menu_visible_before_open": true,
    "show_stock_count": true,
    "max_per_customer": 1,
    "items": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "name": "Example name",
        "item_name": "<item_name>",
        "size_label": "<size_label>",
        "colour_label": "<colour_label>",
        "size_name": "<size_name>",
        "colour_name": "<colour_name>",
        "colour_photo_url": "https://example.com/image.jpg",
        "price_pence": 1500,
        "usual_price_pence": 1500,
        "drop_price_pence": 1500,
        "buyer_price_pence": 1500,
        "image_url": "https://example.com/image.jpg",
        "quantity": 1,
        "remaining": 1,
        "is_unlimited": true,
        "sold": 1,
        "max_per_order": 1,
        "per_bundle": -9007199254740991,
        "sold_out_at": "<sold_out_at>",
        "sort_order": 1,
        "allergens": [
          "<allergen>"
        ],
        "allergens_declared_none": true,
        "allergens_missing": true,
        "is_food": true
      }
    ],
    "windows": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "starts_at": "<starts_at>",
        "ends_at": "<ends_at>",
        "max_orders": 1,
        "orders_remaining": 1,
        "booked_count": 1,
        "booked_names": [
          "<booked_name>"
        ],
        "removable": true,
        "in_dispute": true
      }
    ],
    "moves_not_reached": {
      "not_told": -9007199254740991,
      "not_told_names": [
        "<not_told_name>"
      ]
    },
    "buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "cancelled_buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "orders_count": 1,
    "units_sold": 1,
    "units_total": 1,
    "has_unlimited": true,
    "gross_pence": 1500,
    "still_to_come_pence": 1500,
    "unpaid_orders_count": 1,
    "bundle_price_pence": 1500,
    "bundle_buyer_price_pence": 1500,
    "bundle_total": 1,
    "bundle_remaining": 1,
    "is_bundle": true,
    "blockers": [
      "<blocker>"
    ],
    "blocker_codes": [
      {
        "code": "no_items",
        "names": [
          "Example name"
        ]
      }
    ],
    "making_started_at": "<making_started_at>",
    "created_at": "<created_at>",
    "updated_at": "<updated_at>"
  }
}

Add an order to a drop by hand.

The shop adds an order somebody asked for outside Membber (a DM, a call). Only while the drop is selling and the shop takes cash when they collect. The boxes and the window are taken exactly as a pay-later order takes them. Paid to the shop, never by card: `paid: false` waits for payment at collection, `paid: true` records it paid now. No 5% fee. The person is never texted, emailed, linked to a customer or put on any list. Idempotent on `client_token`. Requires the payments permission.

Parameters

dropIdstringpathrequired

Request body

store_idstring · uuidrequired
client_tokenstring · uuidrequired

One per sheet opening, kept across retries: the same token never adds a second order.

namestringrequired

Their first and last name.

phonestringrequired

Their mobile: 07… or +… Never messaged.

window_idstring · uuidrequired

One of this drop's collection windows.

linesarray of objectoptional

An item drop: this drop's items (`batch_drop_items.id`) and how many.

2 child fields
item_idstring · uuidrequired
quantityintegerrequired
boxesintegeroptional

A box drop: how many boxes, instead of `lines`.

paidbooleanrequired

false: Pay when they collect. true: Already paid.

Response, 200

order_idstringrequired
order_numberstringrequired
statusenumrequired
awaiting_paymentpaid
total_penceintegerrequired

The goods alone: a hand-added order carries no 5%.

collection_codestring, nullablerequired
replayedbooleanrequired

True when this token had already added the order; nothing new was added.

curl -X POST "https://www.membber.com/api/v1/drops/b11f386a-0000-4000-8000-d0c5000000b1/orders" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "client_token": "bf14cc85-0000-4000-8000-d0c5000000bf",
    "name": "Example name",
    "phone": "+44 7700 900123",
    "window_id": "f310cfca-0000-4000-8000-d0c5000000f3",
    "paid": true
  }'
Response, 200
{
  "order_id": "4991ffac-0000-4000-8000-d0c500000049",
  "order_number": "<order_number>",
  "status": "awaiting_payment",
  "total_pence": 1500,
  "collection_code": "EXAMPLE10",
  "replayed": true
}

One drop's prep list.

What to make (each item’s total, split paid and not yet paid, with a box multiplied out into its items) and who is coming in each collection window (buyer, what they bought, collection code, payment). Read from the same orders the drop board counts, so a released, cancelled or refunded order is never on it. Read-only.

Parameters

dropIdstringpathrequired
store_idstring · uuidqueryrequired
drop_idstring · uuidqueryoptional

Response, 200

prepobjectrequired
6 child fields
dropobjectrequired
6 child fields
idstringrequired
namestringrequired
stateenumrequired
draftscheduledliveclosedcollectedcancelled
collection_datestringrequired
timezonestringrequired
is_bundlebooleanrequired
generated_atstringrequired

When this sheet was read. Printed on it, so an old printout says it is old.

to_makearray of objectrequired

One line per thing to make, in the drop's own item order. A box is multiplied out into what goes in it.

6 child fields
keystringrequired
menu_item_idstring, nullablerequired
namestringrequired
totalintegerrequired

Everything to make for this item: paid plus not yet paid.

paidintegerrequired
unpaidintegerrequired
totalsobjectrequired
6 child fields
ordersintegerrequired
paid_ordersintegerrequired
unpaid_ordersintegerrequired
collected_ordersintegerrequired
unitsintegerrequired
unpaid_unitsintegerrequired
windowsarray of objectrequired

Every collection window in time order, each with its orders by buyer name.

4 child fields
idstring, nullablerequired

Null for the orders no collection window could be found for.

starts_atstring, nullablerequired
ends_atstring, nullablerequired
ordersarray of objectrequired
12 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired

The name the buyer gave. Null when they gave none.

paymentenumrequired

`paid` once the money is recorded; `unpaid` for a placed bank-transfer or cash order.

paidunpaid
payment_method_kindstring, nullablerequired
collectedbooleanrequired
didnt_comebooleanrequired

True once the shop marked the order "didn't come" after its collection window. It stays on the sheet (its boxes were made) and is never counted as not yet paid.

collection_codestring, nullablerequired

The code the buyer shows at the counter. Null while it is held (an unpaid bank transfer) or when the order has none.

code_held_until_paidbooleanrequired

True for an unpaid bank transfer: the buyer has not been sent a code yet, so none is printed.

total_penceintegerrequired
linesarray of objectrequired

What the buyer bought, as they bought it. A box stays one line here.

2 child fields
namestringrequired
quantityintegerrequired
added_by_shopbooleanrequired

FA247: the shop added this order by hand. Printed as "Added by you".

left_offobjectrequired

Orders the drop board still counts that are not on this sheet, and why.

1 child field
collection_cancelledintegerrequired
curl -G "https://www.membber.com/api/v1/drops/b11f386a-0000-4000-8000-d0c5000000b1/prep" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066"
Response, 200
{
  "prep": {
    "drop": {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "name": "Example name",
      "state": "draft",
      "collection_date": "<collection_date>",
      "timezone": "Europe/London",
      "is_bundle": true
    },
    "generated_at": "<generated_at>",
    "to_make": [
      {
        "key": "<key>",
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "name": "Example name",
        "total": -9007199254740991,
        "paid": -9007199254740991,
        "unpaid": -9007199254740991
      }
    ],
    "totals": {
      "orders": -9007199254740991,
      "paid_orders": -9007199254740991,
      "unpaid_orders": -9007199254740991,
      "collected_orders": -9007199254740991,
      "units": -9007199254740991,
      "unpaid_units": -9007199254740991
    },
    "windows": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "starts_at": "<starts_at>",
        "ends_at": "<ends_at>",
        "orders": [
          {
            "id": "00000d1b-0000-4000-8000-d0c500000000",
            "order_number": "<order_number>",
            "name": "Example name",
            "payment": "paid",
            "payment_method_kind": "<payment_method_kind>",
            "collected": true,
            "didnt_come": true,
            "collection_code": "EXAMPLE10",
            "code_held_until_paid": true,
            "total_pence": 1500,
            "lines": [
              null
            ],
            "added_by_shop": true
          }
        ]
      }
    ],
    "left_off": {
      "collection_cancelled": -9007199254740991
    }
  }
}

Say you have started making it.

Records that the merchant has started making this drop and queues one message to every buyer on it. OPTIONAL: a drop nobody starts behaves exactly as it does today, and this is never a step anyone must complete. Idempotent, a second call returns `already_started: true` and sends nothing, so a double tap on a flaky connection cannot text everyone twice. Messages are DELIVERED by the sweep rather than by this call, inside the shop’s sending hours, because a baker who starts at 04:40 must not text forty strangers at 04:40. Never carries the collection address or code: the reminder two hours before the window is the message that tells people what to do, and this one is early on purpose.

Parameters

dropIdstringpathrequired

Request body

store_idstring · uuidrequired
drop_idstring · uuidoptional

Response, 200

dropobjectrequired
40 child fields
idstringrequired
store_idstringrequired
codestringrequired

The short code that identifies this drop.

shop_urlstring, nullablerequired

The shop's permanent page, at its own address: https://{handle}.membber.com/

image_urlstring, nullablerequired
share_urlstringrequired

The link to this specific drop.

namestringrequired
descriptionstring, nullablerequired
statusenumrequired
draftscheduledcancelled
stateenumrequired

Computed at read time. Never sent by a client.

draftscheduledliveclosedcollectedcancelled
opens_atstringrequired
closes_atstring, nullablerequired

Null only while a draft has no closing time yet. A scheduled drop always has one.

collection_datestringrequired
timezonestringrequired
checkout_ttl_secondsnumberrequired
menu_visible_before_openbooleanrequired
show_stock_countbooleanrequired
max_per_customernumber, nullablerequired
itemsarray of objectrequired
26 child fields
idstringrequired
menu_item_idstringrequired
namestringrequired

Copied from the menu item at read time, never stored twice. On a line that sells a size or colour: the item’s name with them, "Hoodie, M, Black".

item_namestringoptional

Her item’s own name, on a line that sells one of its sizes or colours (`name` then reads "Hoodie, M, Black").

size_labelstringoptional

The size this line sells, as her item names it. Absent: no size.

colour_labelstringoptional

The colour this line sells, as her item names it. Absent: no colour.

size_namestringoptional

Her own word for this item’s sizes ("Flavour"), when she chose one. Absent: the word for size.

colour_namestringoptional

Her own word for this item’s colours ("Grind"), when she chose one. Absent: the word for colour.

colour_photo_urlstringoptional

The photo she chose for this colour, one of the item’s own photos. Absent: none chosen.

price_pencenumber, nullablerequired

What one sells at in this drop, before Membber's fee: the drop's own price when it has one, else the menu price. What checkout charges.

usual_price_pencenumber, nullableoptional

Her usual (menu) price for one.

drop_price_penceinteger, nullableoptional

This drop's own price for one, or null when it sells at the usual price.

buyer_price_pencenumber, nullablerequired
image_urlstring, nullablerequired
quantitynumberrequired

How many were put up. Meaningless when is_unlimited.

remainingnumberrequired

How many are still available. Never negative. Mirrors quantity when is_unlimited.

is_unlimitedbooleanrequired
soldnumberrequired
max_per_ordernumber, nullablerequired

null = no limit.

per_bundleinteger, nullableoptional

How many of this item go in one box of a one-price drop. Null means one.

sold_out_atstring, nullablerequired

Always null for an unlimited item.

sort_ordernumberrequired
allergensarray of stringrequired
allergens_declared_nonebooleanrequired
allergens_missingbooleanrequired
is_foodbooleanoptional

False for an item that is not food (clothes, flowers): no allergen answer is asked of it. True or absent: food.

windowsarray of objectrequired
9 child fields
idstringrequired

Send it back on the windows PUT to edit this window in place.

starts_atstringrequired
ends_atstringrequired
max_ordersnumber, nullablerequired

null = no cap on orders in this window.

orders_remainingnumber, nullablerequired
booked_countintegeroptional

People still to collect from this window. Moving it needs confirm_booked_changes; removing it is refused.

booked_namesarray of stringoptional

First names of the people still to collect, oldest order first.

removablebooleanoptional

False when anyone holds a place in this window, collected or not: removing it is refused.

in_disputebooleanoptional

True when the only orders holding this window are disputed ones nobody collected. Absent otherwise.

moves_not_reachedobjectoptional

Buyers still to collect whom a moved collection time could not reach. Absent when there are none.

2 child fields
not_toldintegerrequired

How many buyers could not be reached about a moved time.

not_told_namesarray of stringoptional

Their first names, oldest order first, three at most (the most the sentence names). Shorter than not_told when one gave no name or there are more than three; absent when none gave one.

buyersarray of objectoptional
21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

cancelled_buyersarray of objectoptional

Orders refunded in full, disputed by the buyer, released unpaid or cancelled with the drop, newest first. Listed only: never in buyers and never in any count or total.

21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

orders_countnumberrequired
units_soldnumberrequired
units_totalnumberrequired

Sum of the counted items only; an unlimited item adds nothing.

has_unlimitedbooleanrequired
gross_pencenumberrequired

TAKEN: money that has arrived and stayed, from every paid status (paid, ready, collected, part-refunded), net of recorded refunds. Card orders without the buyer fee; cash and transfer in full.

still_to_come_pencenumberrequired
unpaid_orders_countnumberrequired
bundle_price_pencenumber, nullableoptional
bundle_buyer_price_pencenumber, nullableoptional
bundle_totalnumber, nullableoptional

How many boxes were put up.

bundle_remainingnumber, nullableoptional

How many boxes are left. Stock, never writable.

is_bundlebooleanoptional

True when this drop sells one box at one price.

blockersarray of stringrequired
blocker_codesarray of objectoptional
2 child fields
codeenumrequired
no_itemsno_timesno_closesno_quantityno_priceno_allergenstime_before_openstime_before_closestime_off_day
namesarray of stringrequired
making_started_atstring, nullableoptional
created_atstringrequired
updated_atstringrequired
people_to_tellnumberrequired

How many orders are queued to hear, so the app can say "23 people will be told" rather than a vague success.

already_startedbooleanrequired

True when this drop had already been started. The call is a safe no-op and nobody is told twice.

curl -X POST "https://www.membber.com/api/v1/drops/b11f386a-0000-4000-8000-d0c5000000b1/started" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066"
  }'
Response, 200
{
  "drop": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "code": "EXAMPLE10",
    "shop_url": "https://example.com/image.jpg",
    "image_url": "https://example.com/image.jpg",
    "share_url": "https://example.com/image.jpg",
    "name": "Example name",
    "description": "Added at the front desk",
    "status": "draft",
    "state": "draft",
    "opens_at": "<opens_at>",
    "closes_at": "<closes_at>",
    "collection_date": "<collection_date>",
    "timezone": "Europe/London",
    "checkout_ttl_seconds": 1,
    "menu_visible_before_open": true,
    "show_stock_count": true,
    "max_per_customer": 1,
    "items": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "name": "Example name",
        "item_name": "<item_name>",
        "size_label": "<size_label>",
        "colour_label": "<colour_label>",
        "size_name": "<size_name>",
        "colour_name": "<colour_name>",
        "colour_photo_url": "https://example.com/image.jpg",
        "price_pence": 1500,
        "usual_price_pence": 1500,
        "drop_price_pence": 1500,
        "buyer_price_pence": 1500,
        "image_url": "https://example.com/image.jpg",
        "quantity": 1,
        "remaining": 1,
        "is_unlimited": true,
        "sold": 1,
        "max_per_order": 1,
        "per_bundle": -9007199254740991,
        "sold_out_at": "<sold_out_at>",
        "sort_order": 1,
        "allergens": [
          "<allergen>"
        ],
        "allergens_declared_none": true,
        "allergens_missing": true,
        "is_food": true
      }
    ],
    "windows": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "starts_at": "<starts_at>",
        "ends_at": "<ends_at>",
        "max_orders": 1,
        "orders_remaining": 1,
        "booked_count": 1,
        "booked_names": [
          "<booked_name>"
        ],
        "removable": true,
        "in_dispute": true
      }
    ],
    "moves_not_reached": {
      "not_told": -9007199254740991,
      "not_told_names": [
        "<not_told_name>"
      ]
    },
    "buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "cancelled_buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "orders_count": 1,
    "units_sold": 1,
    "units_total": 1,
    "has_unlimited": true,
    "gross_pence": 1500,
    "still_to_come_pence": 1500,
    "unpaid_orders_count": 1,
    "bundle_price_pence": 1500,
    "bundle_buyer_price_pence": 1500,
    "bundle_total": 1,
    "bundle_remaining": 1,
    "is_bundle": true,
    "blockers": [
      "<blocker>"
    ],
    "blocker_codes": [
      {
        "code": "no_items",
        "names": [
          "Example name"
        ]
      }
    ],
    "making_started_at": "<making_started_at>",
    "created_at": "<created_at>",
    "updated_at": "<updated_at>"
  },
  "people_to_tell": 1,
  "already_started": true
}

Set the collection windows.

Replaces the drop collection windows. A drop owns its own windows for ONE day, its `collection_date`: a window on any other day is refused. Send each window's `id` to edit it in place, so the orders booked into it stay with it. Windows can be added while the drop is selling. Removing a window people booked is refused; moving one people are still coming to is refused with 409 BOOKED_WINDOWS_AFFECTED (naming them) until sent with confirm_booked_changes, which moves it and texts or emails each of them once.

Parameters

dropIdstringpathrequired

Request body

store_idstring · uuidrequired
drop_idstring · uuidoptional
windowsarray of objectrequired
4 child fields
idstring · uuid, nullableoptional

The window this edits, in place (its bookings stay on it). Omit for a new window.

starts_atstringrequired
ends_atstringrequired
max_ordersinteger, nullableoptional
confirm_booked_changesbooleanoptional

True once the seller has agreed to move collection times people booked. Without it such a change is refused with 409 BOOKED_WINDOWS_AFFECTED naming them. Each affected buyer is told once.

Response, 200

dropobjectrequired
40 child fields
idstringrequired
store_idstringrequired
codestringrequired

The short code that identifies this drop.

shop_urlstring, nullablerequired

The shop's permanent page, at its own address: https://{handle}.membber.com/

image_urlstring, nullablerequired
share_urlstringrequired

The link to this specific drop.

namestringrequired
descriptionstring, nullablerequired
statusenumrequired
draftscheduledcancelled
stateenumrequired

Computed at read time. Never sent by a client.

draftscheduledliveclosedcollectedcancelled
opens_atstringrequired
closes_atstring, nullablerequired

Null only while a draft has no closing time yet. A scheduled drop always has one.

collection_datestringrequired
timezonestringrequired
checkout_ttl_secondsnumberrequired
menu_visible_before_openbooleanrequired
show_stock_countbooleanrequired
max_per_customernumber, nullablerequired
itemsarray of objectrequired
26 child fields
idstringrequired
menu_item_idstringrequired
namestringrequired

Copied from the menu item at read time, never stored twice. On a line that sells a size or colour: the item’s name with them, "Hoodie, M, Black".

item_namestringoptional

Her item’s own name, on a line that sells one of its sizes or colours (`name` then reads "Hoodie, M, Black").

size_labelstringoptional

The size this line sells, as her item names it. Absent: no size.

colour_labelstringoptional

The colour this line sells, as her item names it. Absent: no colour.

size_namestringoptional

Her own word for this item’s sizes ("Flavour"), when she chose one. Absent: the word for size.

colour_namestringoptional

Her own word for this item’s colours ("Grind"), when she chose one. Absent: the word for colour.

colour_photo_urlstringoptional

The photo she chose for this colour, one of the item’s own photos. Absent: none chosen.

price_pencenumber, nullablerequired

What one sells at in this drop, before Membber's fee: the drop's own price when it has one, else the menu price. What checkout charges.

usual_price_pencenumber, nullableoptional

Her usual (menu) price for one.

drop_price_penceinteger, nullableoptional

This drop's own price for one, or null when it sells at the usual price.

buyer_price_pencenumber, nullablerequired
image_urlstring, nullablerequired
quantitynumberrequired

How many were put up. Meaningless when is_unlimited.

remainingnumberrequired

How many are still available. Never negative. Mirrors quantity when is_unlimited.

is_unlimitedbooleanrequired
soldnumberrequired
max_per_ordernumber, nullablerequired

null = no limit.

per_bundleinteger, nullableoptional

How many of this item go in one box of a one-price drop. Null means one.

sold_out_atstring, nullablerequired

Always null for an unlimited item.

sort_ordernumberrequired
allergensarray of stringrequired
allergens_declared_nonebooleanrequired
allergens_missingbooleanrequired
is_foodbooleanoptional

False for an item that is not food (clothes, flowers): no allergen answer is asked of it. True or absent: food.

windowsarray of objectrequired
9 child fields
idstringrequired

Send it back on the windows PUT to edit this window in place.

starts_atstringrequired
ends_atstringrequired
max_ordersnumber, nullablerequired

null = no cap on orders in this window.

orders_remainingnumber, nullablerequired
booked_countintegeroptional

People still to collect from this window. Moving it needs confirm_booked_changes; removing it is refused.

booked_namesarray of stringoptional

First names of the people still to collect, oldest order first.

removablebooleanoptional

False when anyone holds a place in this window, collected or not: removing it is refused.

in_disputebooleanoptional

True when the only orders holding this window are disputed ones nobody collected. Absent otherwise.

moves_not_reachedobjectoptional

Buyers still to collect whom a moved collection time could not reach. Absent when there are none.

2 child fields
not_toldintegerrequired

How many buyers could not be reached about a moved time.

not_told_namesarray of stringoptional

Their first names, oldest order first, three at most (the most the sentence names). Shorter than not_told when one gave no name or there are more than three; absent when none gave one.

buyersarray of objectoptional
21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

cancelled_buyersarray of objectoptional

Orders refunded in full, disputed by the buyer, released unpaid or cancelled with the drop, newest first. Listed only: never in buyers and never in any count or total.

21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

orders_countnumberrequired
units_soldnumberrequired
units_totalnumberrequired

Sum of the counted items only; an unlimited item adds nothing.

has_unlimitedbooleanrequired
gross_pencenumberrequired

TAKEN: money that has arrived and stayed, from every paid status (paid, ready, collected, part-refunded), net of recorded refunds. Card orders without the buyer fee; cash and transfer in full.

still_to_come_pencenumberrequired
unpaid_orders_countnumberrequired
bundle_price_pencenumber, nullableoptional
bundle_buyer_price_pencenumber, nullableoptional
bundle_totalnumber, nullableoptional

How many boxes were put up.

bundle_remainingnumber, nullableoptional

How many boxes are left. Stock, never writable.

is_bundlebooleanoptional

True when this drop sells one box at one price.

blockersarray of stringrequired
blocker_codesarray of objectoptional
2 child fields
codeenumrequired
no_itemsno_timesno_closesno_quantityno_priceno_allergenstime_before_openstime_before_closestime_off_day
namesarray of stringrequired
making_started_atstring, nullableoptional
created_atstringrequired
updated_atstringrequired
moved_bookingsobjectoptional

Who a move of booked collection times reached.

4 child fields
ordersintegerrequired

Orders whose collection time moved with their window.

toldintegerrequired

Of those, buyers told the new time by text or email.

not_toldintegerrequired

Buyers who could not be told (no mobile or email that worked). The seller should reach them. A buyer whose message is still waiting to be sent is in neither count.

not_told_namesarray of stringoptional

First names of the buyers who could not be told, oldest order first, as given on their orders at this shop (the names `windows[].booked_names` gave before the move), three at most: the most the sentence names, which from four says the first two and counts the rest from `not_told`. A buyer who gave no name is not in it, so it can be shorter than `not_told`. Absent when none of them gave one: say the count.

curl -X PUT "https://www.membber.com/api/v1/drops/b11f386a-0000-4000-8000-d0c5000000b1/windows" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "windows": [
      {
        "starts_at": "<starts_at>",
        "ends_at": "<ends_at>"
      }
    ]
  }'
Response, 200
{
  "drop": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "code": "EXAMPLE10",
    "shop_url": "https://example.com/image.jpg",
    "image_url": "https://example.com/image.jpg",
    "share_url": "https://example.com/image.jpg",
    "name": "Example name",
    "description": "Added at the front desk",
    "status": "draft",
    "state": "draft",
    "opens_at": "<opens_at>",
    "closes_at": "<closes_at>",
    "collection_date": "<collection_date>",
    "timezone": "Europe/London",
    "checkout_ttl_seconds": 1,
    "menu_visible_before_open": true,
    "show_stock_count": true,
    "max_per_customer": 1,
    "items": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "name": "Example name",
        "item_name": "<item_name>",
        "size_label": "<size_label>",
        "colour_label": "<colour_label>",
        "size_name": "<size_name>",
        "colour_name": "<colour_name>",
        "colour_photo_url": "https://example.com/image.jpg",
        "price_pence": 1500,
        "usual_price_pence": 1500,
        "drop_price_pence": 1500,
        "buyer_price_pence": 1500,
        "image_url": "https://example.com/image.jpg",
        "quantity": 1,
        "remaining": 1,
        "is_unlimited": true,
        "sold": 1,
        "max_per_order": 1,
        "per_bundle": -9007199254740991,
        "sold_out_at": "<sold_out_at>",
        "sort_order": 1,
        "allergens": [
          "<allergen>"
        ],
        "allergens_declared_none": true,
        "allergens_missing": true,
        "is_food": true
      }
    ],
    "windows": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "starts_at": "<starts_at>",
        "ends_at": "<ends_at>",
        "max_orders": 1,
        "orders_remaining": 1,
        "booked_count": 1,
        "booked_names": [
          "<booked_name>"
        ],
        "removable": true,
        "in_dispute": true
      }
    ],
    "moves_not_reached": {
      "not_told": -9007199254740991,
      "not_told_names": [
        "<not_told_name>"
      ]
    },
    "buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "cancelled_buyers": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "order_number": "<order_number>",
        "name": "Example name",
        "email": "alex@example.com",
        "total_pence": 1500,
        "paid_at": "<paid_at>",
        "collected_at": "<collected_at>",
        "fulfilment_status": "<fulfilment_status>",
        "collection_at": "<collection_at>",
        "status": "<status>",
        "payment_method_kind": "<payment_method_kind>",
        "offline_payment_due_at": "<offline_payment_due_at>",
        "offline_payment_recorded_at": "<offline_payment_recorded_at>",
        "buyer_fee_pence": 1500,
        "application_fee_pence": 1500,
        "refunded_pence": 1500,
        "stripe_mode": "<stripe_mode>",
        "added_by_shop": true,
        "failure_code": "EXAMPLE10",
        "dispute_due_by": "<dispute_due_by>",
        "dispute_lost_pence": 1500
      }
    ],
    "orders_count": 1,
    "units_sold": 1,
    "units_total": 1,
    "has_unlimited": true,
    "gross_pence": 1500,
    "still_to_come_pence": 1500,
    "unpaid_orders_count": 1,
    "bundle_price_pence": 1500,
    "bundle_buyer_price_pence": 1500,
    "bundle_total": 1,
    "bundle_remaining": 1,
    "is_bundle": true,
    "blockers": [
      "<blocker>"
    ],
    "blocker_codes": [
      {
        "code": "no_items",
        "names": [
          "Example name"
        ]
      }
    ],
    "making_started_at": "<making_started_at>",
    "created_at": "<created_at>",
    "updated_at": "<updated_at>"
  },
  "moved_bookings": {
    "orders": -9007199254740991,
    "told": -9007199254740991,
    "not_told": -9007199254740991,
    "not_told_names": [
      "<not_told_name>"
    ]
  }
}

Be emailed once when a sold-out size and colour is back.

Saves one ask on a size and colour of an item in this drop that is gone: one email when it can be bought again in this drop or the shop's next, then the ask has ended. PUBLIC, no account needed. Does NOT join the shop's list. Every answer but available reads the same, whatever the address. Refused (404) until it is switched on.

auth none, publicop askWhenBackidempotent retryrate limitederrors

Parameters

dropCodestringpathrequired

Request body

drop_line_idstring · uuidrequired
emailstringrequired

Where the one email goes. Required, and exactly one address, or the request is refused.

Response, 200

okbooleanrequired
outcomeenumrequired
savedavailable
curl -X POST "https://www.membber.com/api/v1/drops/back-ask/EXAMPLE10" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "drop_line_id": "b01fc3f6-0000-4000-8000-d0c5000000b0",
    "email": "alex@example.com"
  }'
Response, 200
{
  "ok": true,
  "outcome": "saved"
}

List the people her drops brought her.

Membber Drops only. Everyone who bought from one of her drops, joined her alert list or asked a question, with what they paid, where they came from and whether they are on her alert list. Deleted people are never listed or counted. Names and contacts are the ones this shop was given. Requires Membber Drops.

Parameters

store_idstring · uuidqueryrequired

The shop (also authorises the merchant).

fromenumqueryoptional

Only people who first came through this door.

drop_purchasedrop_alertsdrop_question
facetstringqueryoptional

Comma-separated AND filters: has_ordered, on_alert_list. Loyalty filters answer 400 NOT_IN_DROPS_SCOPE.

drop_idstring · uuidqueryoptional

Narrows has_ordered to people who ordered from this drop.

sortenumqueryoptional

recent (default), paid (most first) or name. Id breaks ties; nameless people sort last.

recentpaidname
qstringqueryoptional

Name, email or mobile this shop was given. Ignored under 2 characters.

limitintegerqueryoptional

Page size (default 30, max 100). 0 returns counts and the total only.

offsetintegerqueryoptional

Page offset.

Response, 200

buyersarray of objectrequired
13 child fields
idstringrequired
namestring, nullablerequired

The latest name typed at her checkout. Null when she was never given one. Never the name on their shared account, which another shop may have been given.

emailstring, nullablerequired

Store-received email. The title when there is no name.

phonestring, nullableoptional

Store-received mobile, as written, and only when this shop has no name and no email for them: it is then the row's title. Null otherwise.

came_fromobjectrequired

How they first came to this shop, whichever door it was. Null only when no first touch was recorded.

3 child fields
viaenumrequired

The door that first brought them to this shop, as recorded. drop_purchase, drop_alerts or drop_question when a drop did; otherwise the door from before her drops (own_app, app_clip, shared_app, merchant, import, web), which clients word with the Business app's came-from words. Clients word it themselves.

drop_purchasedrop_alertsdrop_questionown_appapp_clipshared_appmerchantimportweb
drop_namestring, nullablerequired

The drop that won them, as named at the time. Null when not recorded, and always null for a door that is not a drop.

atstringrequired

When they first met this shop (ISO 8601).

order_countintegerrequired

Drop orders bought (any purchased status, refunds included).

awaiting_payment_countintegerrequired

Drop orders placed and still to be paid at collection.

paid_penceintegerrequired

What they paid on her drops after refunds, including the Membber buyer fee.

awaiting_penceintegerrequired

What is still to pay on placed-but-unpaid drop orders.

last_activity_atstring, nullablerequired

Latest of their last drop order, first visit and alert-list signup.

alertobjectrequired
4 child fields
onbooleanrequired

On her alert list now: joined, and not stopped. A per-drop "Remind me" is never on.

sincestring, nullablerequired

When they said yes. Null when they were never on the list.

channelsarray of enumrequired

How the list reaches them. Empty unless `on`.

smsemail
stopped_atstring, nullablerequired

When they stopped hearing from her. Null while on the list.

blockedbooleanrequired

Wholly blocked by this shop.

blocked_channelsarray of enumrequired

Channels this shop has muted for them.

emailsmsdrops_chatapp_message
countsobjectrequired

Counted after q and drop_id and before from and facets, so each pill shows what pressing it gives.

6 child fields
everyoneintegerrequired
boughtintegerrequired
alertsintegerrequired
askedintegerrequired
has_orderedintegerrequired
on_alert_listintegerrequired
currencystringrequired

The shop's currency, lower-case ISO 4217.

paginationobjectrequired
4 child fields
limitintegerrequired
offsetintegerrequired
has_morebooleanrequired
total_countintegerrequired
curl -G "https://www.membber.com/api/v1/drops/buyers" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066"
Response, 200
{
  "buyers": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "name": "Example name",
      "email": "alex@example.com",
      "phone": "+44 7700 900123",
      "came_from": {
        "via": "drop_purchase",
        "drop_name": "<drop_name>",
        "at": "<at>"
      },
      "order_count": 1,
      "awaiting_payment_count": 1,
      "paid_pence": 1500,
      "awaiting_pence": 1500,
      "last_activity_at": "<last_activity_at>",
      "alert": {
        "on": true,
        "since": "<since>",
        "channels": [
          "sms"
        ],
        "stopped_at": "<stopped_at>"
      },
      "blocked": true,
      "blocked_channels": [
        "email"
      ]
    }
  ],
  "counts": {
    "everyone": -9007199254740991,
    "bought": -9007199254740991,
    "alerts": -9007199254740991,
    "asked": -9007199254740991,
    "has_ordered": -9007199254740991,
    "on_alert_list": -9007199254740991
  },
  "currency": "GBP",
  "pagination": {
    "limit": 25,
    "offset": 0,
    "has_more": true,
    "total_count": 1
  }
}

One person her drops brought her.

Membber Drops only. Their name and contacts as this shop received them, how they first came, their place on her alert list (with the exact words they agreed to), and their drop orders. 404 alike for a deleted person, someone who is not her customer, and someone her drops did not bring. Requires Membber Drops.

Parameters

customerIdstringpathrequired
store_idstring · uuidqueryrequired

The shop (also authorises the merchant).

orders_cursorstringqueryoptional

From `orders_next_cursor`, for the next 25 orders.

Response, 200

idstringrequired
identityobjectrequired
3 child fields
namestring, nullablerequired

Only a name this shop was given at checkout. Null when none; never the name on their shared account.

emailstring, nullablerequired

Only an email this shop was given.

phonestring, nullablerequired

Only a mobile this shop was given.

came_fromobjectrequired

How they first came to this shop, whichever door it was. Null only when no first touch was recorded.

3 child fields
viaenumrequired

The door that first brought them to this shop, as recorded. drop_purchase, drop_alerts or drop_question when a drop did; otherwise the door from before her drops (own_app, app_clip, shared_app, merchant, import, web), which clients word with the Business app's came-from words. Clients word it themselves.

drop_purchasedrop_alertsdrop_questionown_appapp_clipshared_appmerchantimportweb
drop_namestring, nullablerequired

The drop that won them, as named at the time. Null when not recorded, and always null for a door that is not a drop.

atstringrequired

When they first met this shop (ISO 8601).

alertobjectrequired
9 child fields
onbooleanrequired

On her alert list now: joined, and not stopped. A per-drop "Remind me" is never on.

sincestring, nullablerequired

When they said yes. Null when they were never on the list.

channelsarray of enumrequired

How the list reaches them. Empty unless `on`.

smsemail
stopped_atstring, nullablerequired

When they stopped hearing from her. Null while on the list.

sourcestring, nullablerequired

Where they said yes, e.g. shop_page.

wordsstring, nullablerequired

The exact words they agreed to.

last_changeobjectrequired

The latest change to their alerts: who, how and when. Null when they were never on her list.

6 child fields
actionenumrequired
onoff
atstringrequired

When (ISO 8601).

whoenumrequired
themyoushopnot_recorded
howenumrequired

text_link / email_link: the stop link in a text or an email (or their mail app's unsubscribe button); link: a stop link that did not say where it was sent; shop_page: they joined on her shop page; drop_reminder: a Remind me; switch: the seller's switch; contact_corrected: she corrected that contact. Null when not recorded.

text_linkemail_linklinkshop_pagedrop_reminderswitchcontact_corrected
namestring, nullablerequired

The staff member's name when `who` is shop and their profile has one.

they_askedbooleanrequired

She ticked "They asked me to" when turning them back on.

switchenumrequired

What the switch does now. turn_off: on, one tap turns them off. turn_on: off by her own switch, one tap turns them back on. turn_on_if_asked: they stopped them (or nobody recorded who), so only with "They asked me to". none: never on her list, or only on a contact she corrected (they join again on her shop page).

turn_offturn_onturn_on_if_askednone
toarray of stringrequired

Where her alerts reach them now: the mobiles, then the addresses, on her list for them. Empty unless on.

blockobjectrequired

Read-only here; changed in the Business app.

2 child fields
blockedbooleanrequired
channelsarray of enumrequired
emailsmsdrops_chatapp_message
can_messagebooleanrequired

Whether this signed-in person may start a chat with them now. Decided on the server.

data_requestobjectrequired

The latest data request this shop forwarded to Membber for them.

2 child fields
forwarded_atstringrequired
kindenumrequired
copyremove
totalsobjectrequired
5 child fields
order_countintegerrequired

Drop orders bought (any purchased status, disputed and refunded included).

awaiting_payment_countintegerrequired
paid_penceintegerrequired

What they paid on her drops after refunds, including the Membber buyer fee.

paid_order_countintegerrequired

The orders whose money is in paid_pence: settled, and not wholly refunded. Disputed orders are not among them. Use this, not order_count, for "paid on N orders".

awaiting_penceintegerrequired
ordersarray of objectrequired

Their drop orders, newest first, 25 at a time.

12 child fields
idstringrequired
order_numberstring, nullablerequired
batch_drop_idstring, nullablerequired
drop_namestring, nullablerequired
statusstringrequired
total_penceintegerrequired
refunded_penceintegerrequired
created_atstringrequired
collection_atstring, nullablerequired
paid_atstring, nullablerequired
payment_method_kindstring, nullablerequired
dispute_lostbooleanoptional

True when the buyer's bank took the money back: a dispute the shop lost, in full or in part. The order then says "Dispute lost", never "Refunded" (its status still reads refunded or partially_refunded). False for a refund the shop made. Absent on an answer from before 12 Oct 2026.

orders_next_cursorstring, nullablerequired
currencystringrequired
curl -G "https://www.membber.com/api/v1/drops/buyers/a1c10999-0000-4000-8000-d0c5000000a1" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066"
Response, 200
{
  "id": "00000d1b-0000-4000-8000-d0c500000000",
  "identity": {
    "name": "Example name",
    "email": "alex@example.com",
    "phone": "+44 7700 900123"
  },
  "came_from": {
    "via": "drop_purchase",
    "drop_name": "<drop_name>",
    "at": "<at>"
  },
  "alert": {
    "on": true,
    "since": "<since>",
    "channels": [
      "sms"
    ],
    "stopped_at": "<stopped_at>",
    "source": "<source>",
    "words": "<words>",
    "last_change": {
      "action": "on",
      "at": "<at>",
      "who": "them",
      "how": "text_link",
      "name": "Example name",
      "they_asked": true
    },
    "switch": "turn_off",
    "to": [
      "<to>"
    ]
  },
  "block": {
    "blocked": true,
    "channels": [
      "email"
    ]
  },
  "can_message": true,
  "data_request": {
    "forwarded_at": "<forwarded_at>",
    "kind": "copy"
  },
  "totals": {
    "order_count": 1,
    "awaiting_payment_count": 1,
    "paid_pence": 1500,
    "paid_order_count": 1,
    "awaiting_pence": 1500
  },
  "orders": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "order_number": "<order_number>",
      "batch_drop_id": "63a4cc66-0000-4000-8000-d0c500000063",
      "drop_name": "<drop_name>",
      "status": "<status>",
      "total_pence": 1500,
      "refunded_pence": 1500,
      "created_at": "<created_at>",
      "collection_at": "<collection_at>",
      "paid_at": "<paid_at>",
      "payment_method_kind": "<payment_method_kind>",
      "dispute_lost": true
    }
  ],
  "orders_next_cursor": "<orders_next_cursor>",
  "currency": "GBP"
}

Turn a customer's drop alerts off, or back on when they ask.

Membber Drops. Off is immediate. Back on is refused (409 THEY_ASKED_NEEDED) after they stopped alerts themselves unless they_asked is true, and they are then sent one message saying so, with a way to stop. Someone who never joined her alert list is never switched on (409 NOT_ON_ALERT_LIST), and neither is a contact she corrected (409 CONTACT_CORRECTED). Every change is recorded with who made it and how. The same 404 as the customer page for someone who is not hers. Idempotent: setting the state they are already in changes nothing.

Parameters

customerIdstringpathrequired

Request body

store_idstring · uuidoptional

The shop. May travel in the body or as ?store_id=; the authenticated store wins.

onbooleanrequired

true: back on. false: off.

they_askedbooleanoptional

She confirms they asked her to turn their alerts back on. Needed when they stopped them themselves (or nobody recorded who did); recorded with her name and the time.

Response, 200

alertobjectrequired
9 child fields
onbooleanrequired

On her alert list now: joined, and not stopped. A per-drop "Remind me" is never on.

sincestring, nullablerequired

When they said yes. Null when they were never on the list.

channelsarray of enumrequired

How the list reaches them. Empty unless `on`.

smsemail
stopped_atstring, nullablerequired

When they stopped hearing from her. Null while on the list.

sourcestring, nullablerequired

Where they said yes, e.g. shop_page.

wordsstring, nullablerequired

The exact words they agreed to.

last_changeobjectrequired

The latest change to their alerts: who, how and when. Null when they were never on her list.

6 child fields
actionenumrequired
onoff
atstringrequired

When (ISO 8601).

whoenumrequired
themyoushopnot_recorded
howenumrequired

text_link / email_link: the stop link in a text or an email (or their mail app's unsubscribe button); link: a stop link that did not say where it was sent; shop_page: they joined on her shop page; drop_reminder: a Remind me; switch: the seller's switch; contact_corrected: she corrected that contact. Null when not recorded.

text_linkemail_linklinkshop_pagedrop_reminderswitchcontact_corrected
namestring, nullablerequired

The staff member's name when `who` is shop and their profile has one.

they_askedbooleanrequired

She ticked "They asked me to" when turning them back on.

switchenumrequired

What the switch does now. turn_off: on, one tap turns them off. turn_on: off by her own switch, one tap turns them back on. turn_on_if_asked: they stopped them (or nobody recorded who), so only with "They asked me to". none: never on her list, or only on a contact she corrected (they join again on her shop page).

turn_offturn_onturn_on_if_askednone
toarray of stringrequired

Where her alerts reach them now: the mobiles, then the addresses, on her list for them. Empty unless on.

changedintegerrequired

How many of their alert-list sign-ups changed. 0 when they were already that way.

toldenumrequired

Back on after they had stopped them: the ONE message that told them so, on the contact that is back on, with a way to stop. none when nothing was sent (switched off, back on after her own switch, or no way to reach them).

textemailnone
curl -X PATCH "https://www.membber.com/api/v1/drops/buyers/a1c10999-0000-4000-8000-d0c5000000a1/alerts" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "on": true
  }'
Response, 200
{
  "alert": {
    "on": true,
    "since": "<since>",
    "channels": [
      "sms"
    ],
    "stopped_at": "<stopped_at>",
    "source": "<source>",
    "words": "<words>",
    "last_change": {
      "action": "on",
      "at": "<at>",
      "who": "them",
      "how": "text_link",
      "name": "Example name",
      "they_asked": true
    },
    "switch": "turn_off",
    "to": [
      "<to>"
    ]
  },
  "changed": -9007199254740991,
  "told": "text"
}

Pass a buyer's data request on to Membber.

When a buyer asks the shop for a copy of their details, or to be removed, the shop forwards it here. Membber checks it is really them, replies within the legal time and tells the shop. Nothing is deleted or exported automatically. A repeat within 30 days is answered with the first one. Works even if Membber Drops has lapsed.

Parameters

customerIdstringpathrequired

Request body

store_idstring · uuidrequired

The shop (also authorises the merchant).

kindenumrequired

copy: they asked for a copy of their details. remove: they asked to be removed.

copyremove
asked_onstringoptional

The day they asked her (YYYY-MM-DD).

notestringoptional

Anything Membber should know. Sent on, never stored.

Response, 200

forwarded_atstringrequired
kindenumrequired
copyremove
alreadybooleanrequired

True when this shop already forwarded the same request in the last 30 days; nothing new was sent.

referencestring, nullablerequired

A short reference Membber support can find the request by.

curl -X POST "https://www.membber.com/api/v1/drops/buyers/a1c10999-0000-4000-8000-d0c5000000a1/data-request" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "kind": "copy"
  }'
Response, 200
{
  "forwarded_at": "<forwarded_at>",
  "kind": "copy",
  "already": true,
  "reference": "<reference>"
}

Turn a paid order into a chat key, so a buyer is not asked to prove themselves twice.

Authorised by the PaymentIntent's client_secret on a card order, or by the order page token on an order placed to pay later, each held only by the browser that placed the order. Never minted from a remembered email: that is typed, never verified.

auth none, publicop claimDropChatidempotent retryrate limitederrors

Request body

client_secretstringoptional
order_numberstringoptional
order_tokenstringoptional

Response, 200

okbooleanrequired
chat_tokenstringrequired
handlestringrequired
curl -X POST "https://www.membber.com/api/v1/drops/chat/claim" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "client_secret": "<client_secret>",
    "order_number": "<order_number>",
    "order_token": "<order_token>"
  }'
Response, 200
{
  "ok": true,
  "chat_token": "<chat_token>",
  "handle": "<handle>"
}

What her claimed preview knew, for her first drop.

Merchant, her own store (store_id). Her whole preview menu as suggestions she confirms: names, her own words, any price her preview showed (her caption or her post or reel, marked from her post; her own website; or a price we set, marked "Suggested price"), its heading and the photo she kept (in her storage), only until her first drop is published. Never allergens, a day, a time, an address or a way to pay: the preview never held them.

Response, 200

prefillobjectrequired
3 child fields
kindstringrequired
itemsarray of objectrequired
11 child fields
namestringrequired
price_pencenumber, nullablerequired
price_from_postbooleanrequired
price_sourceenumoptional
captionwebsiteinstagram_mediaset_by_us
source_urlstringoptional
price_kindenumoptional
fixedfrom
price_suffixstringoptional
descriptionstring, nullableoptional
categorystring, nullableoptional
enquirebooleanoptional
photo_urlstring, nullablerequired
photosarray of stringrequired
curl -G "https://www.membber.com/api/v1/drops/claimed-preview" \
  -H "Authorization: Bearer $MEMBBER_TOKEN"
Response, 200
{
  "prefill": {
    "kind": "<kind>",
    "items": [
      {
        "name": "Example name",
        "price_pence": 1500,
        "price_from_post": true,
        "price_source": "caption",
        "source_url": "https://example.com/image.jpg",
        "price_kind": "fixed",
        "price_suffix": "<price_suffix>",
        "description": "Added at the front desk",
        "category": "<category>",
        "enquire": true,
        "photo_url": "https://example.com/image.jpg"
      }
    ],
    "photos": [
      "<photo>"
    ]
  }
}

A download's files.

The files of one download, in the seller's order: those ready and those still uploading. Files she removed that can still be brought back are listed apart, in `removed`. Never a link to a file. 404 until downloads are switched on.

auth merchant:readop listDropsDigitalAssetsrate limitederrors

Parameters

store_idstring · uuidqueryoptional
menu_item_idstring · uuidqueryrequired

The download whose files to list.

Response, 200

menu_item_idstring · uuidrequired
is_completebooleanrequired
max_filesintegerrequired
max_bytesintegerrequired
max_video_bytesintegeroptional
max_videosintegeroptional
assetsarray of objectrequired
10 child fields
idstring · uuidrequired
menu_item_idstring · uuidrequired
kindenumrequired
filevideo
original_filenamestringrequired
content_typestringrequired
byte_sizeinteger, nullablerequired
duration_secondsinteger, nullablerequired
statusenumrequired
pendingready
sort_orderintegerrequired
created_atstringrequired
removedarray of objectrequired
9 child fields
idstring · uuidrequired
menu_item_idstring · uuidrequired
kindenumrequired
filevideo
original_filenamestringrequired
content_typestringrequired
byte_sizeinteger, nullablerequired
duration_secondsinteger, nullablerequired
removed_atstringrequired
kept_untilstringrequired
curl -G "https://www.membber.com/api/v1/drops/digital/assets" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "menu_item_id=ca65a6e7-0000-4000-8000-d0c5000000ca"
Response, 200
{
  "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
  "is_complete": true,
  "max_files": -9007199254740991,
  "max_bytes": -9007199254740991,
  "max_video_bytes": -9007199254740991,
  "max_videos": -9007199254740991,
  "assets": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
      "kind": "file",
      "original_filename": "<original_filename>",
      "content_type": "<content_type>",
      "byte_size": -9007199254740991,
      "duration_seconds": -9007199254740991,
      "status": "pending",
      "sort_order": -9007199254740991,
      "created_at": "<created_at>"
    }
  ],
  "removed": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
      "kind": "file",
      "original_filename": "<original_filename>",
      "content_type": "<content_type>",
      "byte_size": -9007199254740991,
      "duration_seconds": -9007199254740991,
      "removed_at": "<removed_at>",
      "kept_until": "<kept_until>"
    }
  ]
}

Start uploading a file to a download.

The server chooses where the file goes and returns a one-use signed upload target; the client sends the bytes straight to storage and then calls confirm. The file is `pending` and counts for nothing until confirmed. A PDF up to 50 MB or an MP4 video up to 50 MB; at most 10 files on one download, of which at most 3 videos (422 TOO_MANY_FILES, 422 TOO_MANY_VIDEOS). A video is sent in parts to `upload.resumable`. 404 until downloads are switched on.

Request body

store_idstring · uuidoptional
menu_item_idstring · uuidrequired

The download this file belongs to.

filenamestringrequired

The file's own name, shown to the seller and later to the buyer. Never used as its storage name.

content_typeenumrequired

A PDF (`application/pdf`) or an MP4 video (`video/mp4`).

application/pdfvideo/mp4
byte_sizeintegerrequired

The size the client is about to send: up to 50 MB for a PDF, up to 50 MB for a video. Checked again, from storage, when the upload is confirmed.

Response, 200

assetobjectrequired
10 child fields
idstring · uuidrequired
menu_item_idstring · uuidrequired
kindenumrequired
filevideo
original_filenamestringrequired
content_typestringrequired
byte_sizeinteger, nullablerequired
duration_secondsinteger, nullablerequired
statusenumrequired
pendingready
sort_orderintegerrequired
created_atstringrequired
uploadobjectrequired
4 child fields
pathstringrequired
tokenstringrequired
signed_urlstringrequired
resumableobjectoptional
3 child fields
endpointstringrequired
bucketstringrequired
chunk_bytesintegerrequired
max_bytesintegerrequired
curl -X POST "https://www.membber.com/api/v1/drops/digital/assets" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
    "filename": "<filename>",
    "content_type": "application/pdf",
    "byte_size": 1
  }'
Response, 200
{
  "asset": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
    "kind": "file",
    "original_filename": "<original_filename>",
    "content_type": "<content_type>",
    "byte_size": -9007199254740991,
    "duration_seconds": -9007199254740991,
    "status": "pending",
    "sort_order": -9007199254740991,
    "created_at": "<created_at>"
  },
  "upload": {
    "path": "<path>",
    "token": "<token>",
    "signed_url": "https://example.com/image.jpg",
    "resumable": {
      "endpoint": "<endpoint>",
      "bucket": "<bucket>",
      "chunk_bytes": -9007199254740991
    }
  },
  "max_bytes": -9007199254740991
}

Remove a file from a download.

The file leaves the download and can be brought back with restore for 30 days (`kept_until`); only after that is its stored file deleted for good. An upload that was never confirmed is deleted at once. Removing the last ready file makes the download incomplete. Removing a file twice is not an error. 404 until downloads are switched on.

Parameters

assetIdstringpathrequired
store_idstring · uuidqueryoptional
asset_idstring · uuidqueryoptional

Response, 200

removedbooleanrequired
asset_idstring · uuidrequired
menu_item_idstring · uuidrequired
is_completebooleanrequired
kept_untilstring, nullablerequired
curl -X DELETE "https://www.membber.com/api/v1/drops/digital/assets/d5fdf82b-0000-4000-8000-d0c5000000d5" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678"
Response, 200
{
  "removed": true,
  "asset_id": "e9c160ca-0000-4000-8000-d0c5000000e9",
  "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
  "is_complete": true,
  "kept_until": "<kept_until>"
}

Confirm an uploaded file.

The server reads the stored object itself: it must exist, be within the size limit of its type, and be that type (a PDF, or an MP4 video) by its declared type AND by its first bytes. A file that fails is deleted at once and the answer is 422 UPLOAD_REFUSED. A file that passes becomes `ready`. Confirming a ready file again returns it unchanged. 404 until downloads are switched on.

Parameters

assetIdstringpathrequired

Request body

store_idstring · uuidoptional
asset_idstring · uuidoptional
duration_secondsintegeroptional

A video only: its length in whole seconds, read on the seller's device from the video's own metadata. Stored and shown, never enforced. Ignored for a PDF.

Response, 200

assetobjectrequired
10 child fields
idstring · uuidrequired
menu_item_idstring · uuidrequired
kindenumrequired
filevideo
original_filenamestringrequired
content_typestringrequired
byte_sizeinteger, nullablerequired
duration_secondsinteger, nullablerequired
statusenumrequired
pendingready
sort_orderintegerrequired
created_atstringrequired
is_completebooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/digital/assets/d5fdf82b-0000-4000-8000-d0c5000000d5/confirm" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "asset_id": "e9c160ca-0000-4000-8000-d0c5000000e9",
    "duration_seconds": 1
  }'
Response, 200
{
  "asset": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
    "kind": "file",
    "original_filename": "<original_filename>",
    "content_type": "<content_type>",
    "byte_size": -9007199254740991,
    "duration_seconds": -9007199254740991,
    "status": "pending",
    "sort_order": -9007199254740991,
    "created_at": "<created_at>"
  },
  "is_complete": true
}

Bring a removed file back.

Undoes a remove: the file is `ready` on its download again, exactly as it was. Works while the stored file is still kept (at least 30 days after the removal) and the download holds fewer than 10 files. 410 FILE_GONE once the stored file has been deleted, 422 TOO_MANY_FILES when the download is full, 422 TOO_MANY_VIDEOS when it is a video and the download already holds 3. Restoring a file that is not removed returns it unchanged. 404 until downloads are switched on.

Parameters

assetIdstringpathrequired

Request body

store_idstring · uuidoptional
asset_idstring · uuidoptional

Response, 200

assetobjectrequired
10 child fields
idstring · uuidrequired
menu_item_idstring · uuidrequired
kindenumrequired
filevideo
original_filenamestringrequired
content_typestringrequired
byte_sizeinteger, nullablerequired
duration_secondsinteger, nullablerequired
statusenumrequired
pendingready
sort_orderintegerrequired
created_atstringrequired
is_completebooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/digital/assets/d5fdf82b-0000-4000-8000-d0c5000000d5/restore" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "asset_id": "e9c160ca-0000-4000-8000-d0c5000000e9"
  }'
Response, 200
{
  "asset": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
    "kind": "file",
    "original_filename": "<original_filename>",
    "content_type": "<content_type>",
    "byte_size": -9007199254740991,
    "duration_seconds": -9007199254740991,
    "status": "pending",
    "sort_order": -9007199254740991,
    "created_at": "<created_at>"
  },
  "is_complete": true
}

Send one free download to an email address.

PUBLIC: the person has no account. For a download the shop made free: emails the address the download's private link, and keeps the sentence it was shown with the claim. Nobody is joined to the shop's list by this call: the address joins when the file is first taken through that link. Not an order. The answer is `{ ok: true }` for every address (new or not, sent now or held back, and for an address the shop has blocked, which is sent nothing) and never carries the link: the link is only in the email. A second claim by the same inbox (its case and any plus-tag ignored) sends the email again, within limits, and leaves the words already stored as they are. One inbox is sent at most 4 of these emails an hour and 8 a day by one shop, and 6 an hour and 12 a day by all shops. Refused when the download has a price, is not complete or has no ready file, when the shop is not live on Drops, and when the sentence is not the server's own. 404 until downloads are switched on.

auth none, publicop claimDropsDigitalFreeidempotent retryrate limitederrors

Parameters

handlestringpathrequired

Request body

handlestringoptional
menu_item_idstring · uuidrequired

The free download to send.

emailstringrequired

Where the download is sent. Required: exactly one address.

consent_textstringrequired

The sentence the person was shown, word for word: "Free when you join {shop}'s list. Unsubscribe any time." with the shop's name in it. A sentence that is not the one the server would store is refused (409 CONSENT_CHANGED).

Response, 200

okbooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/digital/claim/<handle>" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
    "email": "alex@example.com",
    "consent_text": "<consent_text>"
  }'
Response, 200
{
  "ok": true
}

How many of each download sold, were taken free and were downloaded.

One row for each of her downloads that anybody holds: how many were paid for and not closed by a refund or a lost dispute, how many were taken free for an email, and how many times they were downloaded. Her own test copies are counted in none of them. A download she has taken off her menu still answers. 404 until downloads are switched on.

auth merchant:readop listDropsDigitalCountsrate limitederrors

Parameters

store_idstring · uuidqueryoptional

Response, 200

countsarray of objectrequired
4 child fields
menu_item_idstring · uuidrequired
soldintegerrequired
freeintegerrequired
downloadsintegerrequired
curl -G "https://www.membber.com/api/v1/drops/digital/counts" \
  -H "Authorization: Bearer $MEMBBER_TOKEN"
Response, 200
{
  "counts": [
    {
      "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
      "sold": -9007199254740991,
      "free": -9007199254740991,
      "downloads": -9007199254740991
    }
  ]
}

Who may open one download.

The grants of one download, newest first, at most 50: each sale, free claim and seller test, with whether its email was sent, how often it was downloaded and whether it was closed. Never a link or a token. A download she has taken off her menu still answers, because its buyers keep it. 404 until downloads are switched on.

auth merchant:readop listDropsDigitalGrantsrate limitederrors

Parameters

store_idstring · uuidqueryoptional
menu_item_idstring · uuidqueryrequired

The download whose grants to list.

Response, 200

menu_item_idstring · uuidrequired
download_capintegerrequired
grantsarray of objectrequired
15 child fields
idstring · uuidrequired
menu_item_idstring · uuidrequired
sourceenumrequired
purchasefree_claimseller_test
emailstring, nullablerequired
order_numberstring, nullablerequired
granted_atstringrequired
statusenumrequired
openrevoked
revoked_atstring, nullablerequired
revoked_reasonenumrequired
refundeddisputedsellersupport
download_countintegerrequired
download_totalintegerrequired
last_downloaded_atstring, nullablerequired
cappedbooleanrequired
delivery_sent_atstring, nullablerequired
delivery_attemptsintegerrequired
curl -G "https://www.membber.com/api/v1/drops/digital/grants" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "menu_item_id=ca65a6e7-0000-4000-8000-d0c5000000ca"
Response, 200
{
  "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
  "download_cap": -9007199254740991,
  "grants": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
      "source": "purchase",
      "email": "alex@example.com",
      "order_number": "<order_number>",
      "granted_at": "<granted_at>",
      "status": "open",
      "revoked_at": "<revoked_at>",
      "revoked_reason": "refunded",
      "download_count": 1,
      "download_total": -9007199254740991,
      "last_downloaded_at": "<last_downloaded_at>",
      "capped": true,
      "delivery_sent_at": "<delivery_sent_at>",
      "delivery_attempts": -9007199254740991
    }
  ]
}

Download one file of a download you were sent.

PUBLIC: a buyer has no account. The token from the email is the credential and is checked on every call. Counts one download and returns a signed address for ONE named file, good for 10 minutes. The file must be a ready file of this grant's own download: another download's or another shop's file is 404. A wrong, missing or expired token and a closed grant are all the same 404. 429 DOWNLOAD_LIMIT after 20 downloads: the shop can send it again. 404 until downloads are switched on. A FREE download claimed for an email joins its address to the shop's list on the first take (this call proves the address: it carries the token from the email), and that one answer carries `joined_list.leave_path`.

auth none, publicop takeDropsDigitalDownloadidempotent retryrate limitederrors

Parameters

grantIdstringpathrequired

Request body

tstringrequired

The access token from the link in the email (its `t` value).

asset_idstring · uuidrequired

The file to download: one of this grant's own download's ready files.

Response, 200

urlstringrequired
expires_in_secondsintegerrequired
filenamestringrequired
downloads_leftintegerrequired
joined_listobjectoptional

Only for a FREE download claimed for an email, and only on the take that joined the address to the shop's list (the first take; never again for someone already on the list or who has left since). Absent on every other take and for every download that was paid for.

1 child field
leave_pathstringrequired

The shop list's own stop page for this person, as a path on this site (`/u/{token}`). A POST to it stops the messages.

curl -X POST "https://www.membber.com/api/v1/drops/digital/grants/10b4f357-0000-4000-8000-d0c500000010/download" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "t": "<t>",
    "asset_id": "e9c160ca-0000-4000-8000-d0c5000000e9"
  }'
Response, 200
{
  "url": "https://example.com/image.jpg",
  "expires_in_seconds": -9007199254740991,
  "filename": "<filename>",
  "downloads_left": -9007199254740991,
  "joined_list": {
    "leave_path": "<leave_path>"
  }
}

Play one video of a download you were sent.

PUBLIC: a buyer has no account. The token from the email is the credential and is checked on every call. Returns a signed address the page plays ONE named video from, good for an hour. It counts nothing: playing does not use up downloads. A POST, never made when the page is opened, so a mail scanner that follows the link is handed no address. The video must be a ready video of this grant's own download: a PDF, another download's or another shop's file is 404. A wrong, missing or expired token and a closed grant (refunded, disputed) are all the same 404. 429 RATE_LIMITED after 20 addresses for one grant in ten minutes. 404 until downloads are switched on.

auth none, publicop playDropsDigitalVideoidempotent retryrate limitederrors

Parameters

grantIdstringpathrequired

Request body

tstringrequired

The access token from the link in the email (its `t` value).

asset_idstring · uuidrequired

The video to play: one of this grant's own download's ready videos.

Response, 200

urlstringrequired
expires_in_secondsintegerrequired
joined_listobjectoptional

Only for a FREE download claimed for an email, and only on the call that joined the address to the shop's list (watching it is taking it). Absent on every other call and for every download that was paid for.

1 child field
leave_pathstringrequired

The shop list's own stop page for this person, as a path on this site (`/u/{token}`).

curl -X POST "https://www.membber.com/api/v1/drops/digital/grants/10b4f357-0000-4000-8000-d0c500000010/play" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "t": "<t>",
    "asset_id": "e9c160ca-0000-4000-8000-d0c5000000e9"
  }'
Response, 200
{
  "url": "https://example.com/image.jpg",
  "expires_in_seconds": -9007199254740991,
  "joined_list": {
    "leave_path": "<leave_path>"
  }
}

Send a grant's email again.

Sends the same email again, to the same address, with a fresh link good for a year, and sets the download count back to 0 so a buyer who reached the limit can download again. 409 GRANT_REVOKED for a closed grant, 409 NO_ADDRESS when the buyer's details were deleted, 429 SEND_LIMIT after ten sends, 502 EMAIL_FAILED when the email could not be sent. 404 until downloads are switched on.

Parameters

grantIdstringpathrequired

Request body

store_idstring · uuidoptional
grant_idstring · uuidoptional

Response, 200

grantobjectrequired
15 child fields
idstring · uuidrequired
menu_item_idstring · uuidrequired
sourceenumrequired
purchasefree_claimseller_test
emailstring, nullablerequired
order_numberstring, nullablerequired
granted_atstringrequired
statusenumrequired
openrevoked
revoked_atstring, nullablerequired
revoked_reasonenumrequired
refundeddisputedsellersupport
download_countintegerrequired
download_totalintegerrequired
last_downloaded_atstring, nullablerequired
cappedbooleanrequired
delivery_sent_atstring, nullablerequired
delivery_attemptsintegerrequired
curl -X POST "https://www.membber.com/api/v1/drops/digital/grants/10b4f357-0000-4000-8000-d0c500000010/resend" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "grant_id": "05e9cb1e-0000-4000-8000-d0c500000005"
  }'
Response, 200
{
  "grant": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
    "source": "purchase",
    "email": "alex@example.com",
    "order_number": "<order_number>",
    "granted_at": "<granted_at>",
    "status": "open",
    "revoked_at": "<revoked_at>",
    "revoked_reason": "refunded",
    "download_count": 1,
    "download_total": -9007199254740991,
    "last_downloaded_at": "<last_downloaded_at>",
    "capped": true,
    "delivery_sent_at": "<delivery_sent_at>",
    "delivery_attempts": -9007199254740991
  }
}

Send me a test copy of a download.

Emails the signed-in seller the same email a buyer gets, with a working link to the download's private page, at her own sign-in address and no other. The first call makes her test grant; every later call sends it again and sets its download count back to 0. The download must hold at least one ready file (422 NO_FILES). 404 until downloads are switched on.

Request body

store_idstring · uuidoptional
menu_item_idstring · uuidrequired

The download to send a test copy of.

Response, 200

grantobjectrequired
15 child fields
idstring · uuidrequired
menu_item_idstring · uuidrequired
sourceenumrequired
purchasefree_claimseller_test
emailstring, nullablerequired
order_numberstring, nullablerequired
granted_atstringrequired
statusenumrequired
openrevoked
revoked_atstring, nullablerequired
revoked_reasonenumrequired
refundeddisputedsellersupport
download_countintegerrequired
download_totalintegerrequired
last_downloaded_atstring, nullablerequired
cappedbooleanrequired
delivery_sent_atstring, nullablerequired
delivery_attemptsintegerrequired
sent_tostringrequired
curl -X POST "https://www.membber.com/api/v1/drops/digital/grants/test-copy" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca"
  }'
Response, 200
{
  "grant": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
    "source": "purchase",
    "email": "alex@example.com",
    "order_number": "<order_number>",
    "granted_at": "<granted_at>",
    "status": "open",
    "revoked_at": "<revoked_at>",
    "revoked_reason": "refunded",
    "download_count": 1,
    "download_total": -9007199254740991,
    "last_downloaded_at": "<last_downloaded_at>",
    "capped": true,
    "delivery_sent_at": "<delivery_sent_at>",
    "delivery_attempts": -9007199254740991
  },
  "sent_to": "<sent_to>"
}

Her download orders, with the money each one recorded.

Every paid order for a download, newest first, at most 50, with the price, the 5% the buyer paid on top, what went back and what she kept, each a figure the order recorded; whether it could not be sent; and its grant, for Send again. The totals count every download order, listed or not. A download order belongs to no drop, so this is where it shows. Never a link or a token. 404 until downloads are switched on.

auth merchant:readop listDropsDigitalOrdersrate limitederrors

Parameters

store_idstring · uuidqueryoptional

Response, 200

ordersarray of objectrequired
19 child fields
idstring · uuidrequired
order_numberstring, nullablerequired
paid_atstringrequired
statusstringrequired
stateenumrequired
paidpart_refundedrefundeddisputeddispute_lost
menu_item_idstring · uuid, nullablerequired
item_namestring, nullablerequired
emailstring, nullablerequired
goods_penceinteger, nullablerequired
buyer_fee_penceinteger, nullablerequired
total_penceintegerrequired
refunded_penceintegerrequired
dispute_lost_penceintegerrequired
taken_penceintegerrequired
stripe_modestring, nullablerequired
not_sentbooleanrequired
grant_idstring · uuid, nullablerequired
grant_statusenumrequired
openrevoked
downloadsintegerrequired
orders_countintegerrequired
taken_penceintegerrequired
given_back_penceintegerrequired
not_sent_countintegerrequired
curl -G "https://www.membber.com/api/v1/drops/digital/orders" \
  -H "Authorization: Bearer $MEMBBER_TOKEN"
Response, 200
{
  "orders": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "order_number": "<order_number>",
      "paid_at": "<paid_at>",
      "status": "<status>",
      "state": "paid",
      "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
      "item_name": "<item_name>",
      "email": "alex@example.com",
      "goods_pence": 1500,
      "buyer_fee_pence": 1500,
      "total_pence": 1500,
      "refunded_pence": 1500,
      "dispute_lost_pence": 1500,
      "taken_pence": 1500,
      "stripe_mode": "<stripe_mode>",
      "not_sent": true,
      "grant_id": "05e9cb1e-0000-4000-8000-d0c500000005",
      "grant_status": "open",
      "downloads": -9007199254740991
    }
  ],
  "orders_count": 1,
  "taken_pence": 1500,
  "given_back_pence": 1500,
  "not_sent_count": 1
}

Send a paid download that could not be sent.

For a download order that is paid for and flagged as not sent: tries again to make the buyer's grant and send its one email and the receipt, exactly as the payment would have. Safe to call again: a grant that exists is found, and an email that went is not sent twice. Answers the order as it now stands. 409 ORDER_NOT_SENDABLE for an order that is refunded, disputed or not paid, 502 DOWNLOAD_NOT_SENT when it still could not be sent (refund it instead). An order that was already sent is answered as it is: use the grant's own resend to email it again. 404 until downloads are switched on.

Parameters

orderIdstringpathrequired

Request body

store_idstring · uuidoptional
order_idstring · uuidoptional

Response, 200

orderobjectrequired
19 child fields
idstring · uuidrequired
order_numberstring, nullablerequired
paid_atstringrequired
statusstringrequired
stateenumrequired
paidpart_refundedrefundeddisputeddispute_lost
menu_item_idstring · uuid, nullablerequired
item_namestring, nullablerequired
emailstring, nullablerequired
goods_penceinteger, nullablerequired
buyer_fee_penceinteger, nullablerequired
total_penceintegerrequired
refunded_penceintegerrequired
dispute_lost_penceintegerrequired
taken_penceintegerrequired
stripe_modestring, nullablerequired
not_sentbooleanrequired
grant_idstring · uuid, nullablerequired
grant_statusenumrequired
openrevoked
downloadsintegerrequired
curl -X POST "https://www.membber.com/api/v1/drops/digital/orders/b80cf509-0000-4000-8000-d0c5000000b8/send" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "order_id": "4991ffac-0000-4000-8000-d0c500000049"
  }'
Response, 200
{
  "order": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "order_number": "<order_number>",
    "paid_at": "<paid_at>",
    "status": "<status>",
    "state": "paid",
    "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
    "item_name": "<item_name>",
    "email": "alex@example.com",
    "goods_pence": 1500,
    "buyer_fee_pence": 1500,
    "total_pence": 1500,
    "refunded_pence": 1500,
    "dispute_lost_pence": 1500,
    "taken_pence": 1500,
    "stripe_mode": "<stripe_mode>",
    "not_sent": true,
    "grant_id": "05e9cb1e-0000-4000-8000-d0c500000005",
    "grant_status": "open",
    "downloads": -9007199254740991
  }
}

Start the payment for one download.

PUBLIC: a buyer has no account. Creates the order and its PaymentIntent for ONE download and returns the client secret, so the card or wallet is taken on the shop's own page. The same order and payment path as a drop: paying is what sends the file, by email, at once. The server decides from the item whether a line is a download; a basket that mixes a download with anything else is refused. The price is the shop's price with the 5% on top. Refused, with nothing charged, without `immediate_supply_consent`, without an email, with a cash or bank transfer method, a promo code or a gift card, when the download has no ready file, or when the shop is not live on Drops with its own direct-charge Stripe account. 404 until downloads are switched on.

auth none, publicop startDropsDigitalPaymentidempotent retryrate limitederrors

Request body

store_codestringrequired
idempotency_keystringrequired
itemsarray of objectrequired

One line, quantity 1, in this release. Anything else is refused with a sentence.

3 child fields
menu_item_idstring · uuidrequired
quantityintegerrequired
shown_price_penceinteger, nullableoptional

The shop's price for the download the buyer was shown, before the 5%. Any difference from the current price is refused with 409 PRICE_CHANGED and the current price.

customerobjectrequired
2 child fields
emailstringrequired

Where the download is sent. Required.

namestring, nullableoptional
immediate_supply_consentbooleanrequired

True when the buyer ticked "Send it now. I know I can't cancel once it's sent.". Without it the payment is refused (400 CONSENT_REQUIRED) and nothing is charged.

device_fingerprintstring, nullableoptional
payment_methodstring, nullableoptional

Leave out. A download is paid for by card or wallet: anything but `pay_now` is refused.

promo_codestring, nullableoptional

Leave out. A code cannot be used on a download in this release: one that is sent is refused.

gift_card_codestring, nullableoptional

Leave out. A gift card cannot pay for a download in this release: one that is sent is refused.

Response, 200

order_idstringrequired
order_numberstringrequired
statusstringrequired
requires_paymentbooleanrequired
client_secretstring, nullablerequired
connected_account_idstringrequired
publishable_keystring, nullablerequired
charge_modelstringrequired
amount_pencenumberrequired
currencystringrequired
merchant_namestringrequired
curl -X POST "https://www.membber.com/api/v1/drops/digital/payment" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_code": "EXAMPLE10",
    "idempotency_key": "1f0e2d3c-4b5a-4678-9abc-def012345678",
    "items": [
      {
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "quantity": 1
      }
    ],
    "customer": {
      "email": "alex@example.com"
    },
    "immediate_supply_consent": true
  }'
Response, 200
{
  "order_id": "4991ffac-0000-4000-8000-d0c500000049",
  "order_number": "<order_number>",
  "status": "<status>",
  "requires_payment": true,
  "client_secret": "<client_secret>",
  "connected_account_id": "231b6f63-0000-4000-8000-d0c500000023",
  "publishable_key": "<publishable_key>",
  "charge_model": "<charge_model>",
  "amount_pence": 1500,
  "currency": "GBP",
  "merchant_name": "<merchant_name>"
}

Every gift card this shop has sold, with real totals.

Cards sold, balances outstanding (what the shop still owes in goods), spent to date, expired unspent and refunded. Newest first.

Parameters

store_idstring · uuidqueryoptional
limitintegerqueryoptional

Response, 200

availablebooleanrequired
summaryobjectrequired
6 child fields
cards_soldintegerrequired
sold_penceintegerrequired
outstanding_penceintegerrequired
spent_penceintegerrequired
expired_unspent_penceintegerrequired
refunded_penceintegerrequired
cardsarray of objectrequired
18 child fields
idstring · uuidrequired
code_last4stringrequired
statusenumrequired
activevoidedrefundedfrozenexpiredpending
initial_penceintegerrequired
balance_penceintegerrequired
spent_penceintegerrequired
currencystringrequired
purchaser_namestringrequired
purchaser_emailstringrequired
recipient_namestring, nullablerequired
recipient_emailstring, nullablerequired
messagestring, nullablerequired
expires_atstring, nullablerequired
bought_atstringrequired
delivered_atstring, nullablerequired
closed_atstring, nullablerequired
closed_reasonstring, nullablerequired
refunded_penceintegerrequired
curl -G "https://www.membber.com/api/v1/drops/gift-cards" \
  -H "Authorization: Bearer $MEMBBER_TOKEN"
Response, 200
{
  "available": true,
  "summary": {
    "cards_sold": -9007199254740991,
    "sold_pence": 1500,
    "outstanding_pence": 1500,
    "spent_pence": 1500,
    "expired_unspent_pence": 1500,
    "refunded_pence": 1500
  },
  "cards": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "code_last4": "EXAMPLE10",
      "status": "active",
      "initial_pence": 1500,
      "balance_pence": 1500,
      "spent_pence": 1500,
      "currency": "GBP",
      "purchaser_name": "<purchaser_name>",
      "purchaser_email": "alex@example.com",
      "recipient_name": "<recipient_name>",
      "recipient_email": "alex@example.com",
      "message": "Added at the front desk",
      "expires_at": "<expires_at>",
      "bought_at": "<bought_at>",
      "delivered_at": "<delivered_at>",
      "closed_at": "<closed_at>",
      "closed_reason": "Added at the front desk",
      "refunded_pence": 1500
    }
  ]
}

One gift card and every movement on it.

The card (last four only) and its ledger, newest first, with the order each spend went on.

Parameters

giftCardIdstringpathrequired
store_idstring · uuidqueryoptional
gift_card_idstring · uuidqueryoptional

Response, 200

cardobjectrequired
18 child fields
idstring · uuidrequired
code_last4stringrequired
statusenumrequired
activevoidedrefundedfrozenexpiredpending
initial_penceintegerrequired
balance_penceintegerrequired
spent_penceintegerrequired
currencystringrequired
purchaser_namestringrequired
purchaser_emailstringrequired
recipient_namestring, nullablerequired
recipient_emailstring, nullablerequired
messagestring, nullablerequired
expires_atstring, nullablerequired
bought_atstringrequired
delivered_atstring, nullablerequired
closed_atstring, nullablerequired
closed_reasonstring, nullablerequired
refunded_penceintegerrequired
ledgerarray of objectrequired
6 child fields
kindstringrequired
amount_penceintegerrequired
balance_after_penceintegerrequired
order_numberstring, nullablerequired
notestring, nullablerequired
atstringrequired
curl -G "https://www.membber.com/api/v1/drops/gift-cards/ed89b19b-0000-4000-8000-d0c5000000ed" \
  -H "Authorization: Bearer $MEMBBER_TOKEN"
Response, 200
{
  "card": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "code_last4": "EXAMPLE10",
    "status": "active",
    "initial_pence": 1500,
    "balance_pence": 1500,
    "spent_pence": 1500,
    "currency": "GBP",
    "purchaser_name": "<purchaser_name>",
    "purchaser_email": "alex@example.com",
    "recipient_name": "<recipient_name>",
    "recipient_email": "alex@example.com",
    "message": "Added at the front desk",
    "expires_at": "<expires_at>",
    "bought_at": "<bought_at>",
    "delivered_at": "<delivered_at>",
    "closed_at": "<closed_at>",
    "closed_reason": "Added at the front desk",
    "refunded_pence": 1500
  },
  "ledger": [
    {
      "kind": "<kind>",
      "amount_pence": 1500,
      "balance_after_pence": 1500,
      "order_number": "<order_number>",
      "note": "Added at the front desk",
      "at": "<at>"
    }
  ]
}

Refund what is left on a gift card to the card it was bought with.

The unspent balance is refunded to the buyer on the shop's Stripe account and the card is closed. If Stripe refuses, the card is reopened with its balance. Needs the refund permission.

Parameters

giftCardIdstringpathrequired

Request body

store_idstring · uuidoptional
gift_card_idstring · uuidoptional
reasonstring, nullableoptional
attempt_idstringoptional

Response, 200

cardobjectrequired
18 child fields
idstring · uuidrequired
code_last4stringrequired
statusenumrequired
activevoidedrefundedfrozenexpiredpending
initial_penceintegerrequired
balance_penceintegerrequired
spent_penceintegerrequired
currencystringrequired
purchaser_namestringrequired
purchaser_emailstringrequired
recipient_namestring, nullablerequired
recipient_emailstring, nullablerequired
messagestring, nullablerequired
expires_atstring, nullablerequired
bought_atstringrequired
delivered_atstring, nullablerequired
closed_atstring, nullablerequired
closed_reasonstring, nullablerequired
refunded_penceintegerrequired
ledgerarray of objectrequired
6 child fields
kindstringrequired
amount_penceintegerrequired
balance_after_penceintegerrequired
order_numberstring, nullablerequired
notestring, nullablerequired
atstringrequired
curl -X POST "https://www.membber.com/api/v1/drops/gift-cards/ed89b19b-0000-4000-8000-d0c5000000ed/refund" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "gift_card_id": "5c203c9b-0000-4000-8000-d0c50000005c",
    "reason": "Added at the front desk",
    "attempt_id": "70148fcd-0000-4000-8000-d0c500000070"
  }'
Response, 200
{
  "card": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "code_last4": "EXAMPLE10",
    "status": "active",
    "initial_pence": 1500,
    "balance_pence": 1500,
    "spent_pence": 1500,
    "currency": "GBP",
    "purchaser_name": "<purchaser_name>",
    "purchaser_email": "alex@example.com",
    "recipient_name": "<recipient_name>",
    "recipient_email": "alex@example.com",
    "message": "Added at the front desk",
    "expires_at": "<expires_at>",
    "bought_at": "<bought_at>",
    "delivered_at": "<delivered_at>",
    "closed_at": "<closed_at>",
    "closed_reason": "Added at the front desk",
    "refunded_pence": 1500
  },
  "ledger": [
    {
      "kind": "<kind>",
      "amount_pence": 1500,
      "balance_after_pence": 1500,
      "order_number": "<order_number>",
      "note": "Added at the front desk",
      "at": "<at>"
    }
  ]
}

Email the gift card again.

To the same person, with the same code. Only for a card that can still be spent.

Parameters

giftCardIdstringpathrequired

Request body

store_idstring · uuidoptional
gift_card_idstring · uuidoptional

Response, 200

cardobjectrequired
18 child fields
idstring · uuidrequired
code_last4stringrequired
statusenumrequired
activevoidedrefundedfrozenexpiredpending
initial_penceintegerrequired
balance_penceintegerrequired
spent_penceintegerrequired
currencystringrequired
purchaser_namestringrequired
purchaser_emailstringrequired
recipient_namestring, nullablerequired
recipient_emailstring, nullablerequired
messagestring, nullablerequired
expires_atstring, nullablerequired
bought_atstringrequired
delivered_atstring, nullablerequired
closed_atstring, nullablerequired
closed_reasonstring, nullablerequired
refunded_penceintegerrequired
ledgerarray of objectrequired
6 child fields
kindstringrequired
amount_penceintegerrequired
balance_after_penceintegerrequired
order_numberstring, nullablerequired
notestring, nullablerequired
atstringrequired
curl -X POST "https://www.membber.com/api/v1/drops/gift-cards/ed89b19b-0000-4000-8000-d0c5000000ed/resend" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "gift_card_id": "5c203c9b-0000-4000-8000-d0c50000005c"
  }'
Response, 200
{
  "card": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "code_last4": "EXAMPLE10",
    "status": "active",
    "initial_pence": 1500,
    "balance_pence": 1500,
    "spent_pence": 1500,
    "currency": "GBP",
    "purchaser_name": "<purchaser_name>",
    "purchaser_email": "alex@example.com",
    "recipient_name": "<recipient_name>",
    "recipient_email": "alex@example.com",
    "message": "Added at the front desk",
    "expires_at": "<expires_at>",
    "bought_at": "<bought_at>",
    "delivered_at": "<delivered_at>",
    "closed_at": "<closed_at>",
    "closed_reason": "Added at the front desk",
    "refunded_pence": 1500
  },
  "ledger": [
    {
      "kind": "<kind>",
      "amount_pence": 1500,
      "balance_after_pence": 1500,
      "order_number": "<order_number>",
      "note": "Added at the front desk",
      "at": "<at>"
    }
  ]
}

Close a gift card with nothing paid back.

The remaining balance is removed and the reason kept. Needs the refund permission.

Parameters

giftCardIdstringpathrequired

Request body

store_idstring · uuidoptional
gift_card_idstring · uuidoptional
reasonstring, nullableoptional
attempt_idstringoptional

Response, 200

cardobjectrequired
18 child fields
idstring · uuidrequired
code_last4stringrequired
statusenumrequired
activevoidedrefundedfrozenexpiredpending
initial_penceintegerrequired
balance_penceintegerrequired
spent_penceintegerrequired
currencystringrequired
purchaser_namestringrequired
purchaser_emailstringrequired
recipient_namestring, nullablerequired
recipient_emailstring, nullablerequired
messagestring, nullablerequired
expires_atstring, nullablerequired
bought_atstringrequired
delivered_atstring, nullablerequired
closed_atstring, nullablerequired
closed_reasonstring, nullablerequired
refunded_penceintegerrequired
ledgerarray of objectrequired
6 child fields
kindstringrequired
amount_penceintegerrequired
balance_after_penceintegerrequired
order_numberstring, nullablerequired
notestring, nullablerequired
atstringrequired
curl -X POST "https://www.membber.com/api/v1/drops/gift-cards/ed89b19b-0000-4000-8000-d0c5000000ed/void" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "gift_card_id": "5c203c9b-0000-4000-8000-d0c50000005c",
    "reason": "Added at the front desk",
    "attempt_id": "70148fcd-0000-4000-8000-d0c500000070"
  }'
Response, 200
{
  "card": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "code_last4": "EXAMPLE10",
    "status": "active",
    "initial_pence": 1500,
    "balance_pence": 1500,
    "spent_pence": 1500,
    "currency": "GBP",
    "purchaser_name": "<purchaser_name>",
    "purchaser_email": "alex@example.com",
    "recipient_name": "<recipient_name>",
    "recipient_email": "alex@example.com",
    "message": "Added at the front desk",
    "expires_at": "<expires_at>",
    "bought_at": "<bought_at>",
    "delivered_at": "<delivered_at>",
    "closed_at": "<closed_at>",
    "closed_reason": "Added at the front desk",
    "refunded_pence": 1500
  },
  "ledger": [
    {
      "kind": "<kind>",
      "amount_pence": 1500,
      "balance_after_pence": 1500,
      "order_number": "<order_number>",
      "note": "Added at the front desk",
      "at": "<at>"
    }
  ]
}

Whether this shop sells gift cards, and on what terms.

PUBLIC. What the shop page needs to offer a gift card: whether it is on sale (and why not), the amounts, the expiry and the terms printed on the card.

auth none, publicop getDropsGiftCardShoprate limitederrors

Parameters

handlestringpathrequired

Response, 200

on_salebooleanrequired
unavailable_reasonstring, nullablerequired
accepts_gift_cardsbooleanrequired
shop_namestringrequired
currencystringrequired
min_penceintegerrequired
max_penceintegerrequired
preset_pencearray of integerrequired
message_maxintegerrequired
expiry_monthsinteger, nullablerequired
termsstringrequired
curl -G "https://www.membber.com/api/v1/drops/gift-cards/public/<handle>"
Response, 200
{
  "on_sale": true,
  "unavailable_reason": "Added at the front desk",
  "accepts_gift_cards": true,
  "shop_name": "<shop_name>",
  "currency": "GBP",
  "min_pence": 1500,
  "max_pence": 1500,
  "preset_pence": [
    1500
  ],
  "message_max": -9007199254740991,
  "expiry_months": -9007199254740991,
  "terms": "<terms>"
}

What is left on a gift card.

PUBLIC and held down hard (it is the one place a guessed code gets an answer). A wrong code and another shop's code get the same answer.

auth none, publicop checkDropsGiftCardBalanceidempotent retryrate limitederrors

Parameters

handlestringpathrequired

Request body

codestringrequired

Response, 200

shop_namestringrequired
statusenumrequired
activevoidedrefundedfrozenexpiredpending
balance_penceintegerrequired
currencystringrequired
expires_atstring, nullablerequired
usablebooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/gift-cards/public/<handle>/balance" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "EXAMPLE10"
  }'
Response, 200
{
  "shop_name": "<shop_name>",
  "status": "active",
  "balance_pence": 1500,
  "currency": "GBP",
  "expires_at": "<expires_at>",
  "usable": true
}

Start paying for a gift card.

PUBLIC. Creates a pending card and a PaymentIntent on the shop's own Stripe account. The card becomes spendable, and is emailed, only when the payment lands. Replaying the same idempotency key returns the same PaymentIntent.

auth none, publicop startDropsGiftCardPurchaseidempotent retryrate limitederrors

Parameters

handlestringpathrequired

Request body

amount_penceintegerrequired
idempotency_keystringrequired
purchaserobjectrequired
2 child fields
namestringrequired
emailstringrequired

The buyer's email: where the receipt goes, and the card too unless it is for someone else. Required, and exactly one address, or the request is refused.

recipientobjectoptional
2 child fields
namestring, nullableoptional
emailstring, nullableoptional

Where the card is emailed when it is for someone else. Optional: leave it out, or send null or an empty string, and the card goes to the buyer. Anything else must be exactly one address, or the request is refused.

messagestring, nullableoptional

Response, 200

gift_card_idstring · uuidrequired
client_secretstringrequired
publishable_keystring, nullablerequired
connected_account_idstringrequired
charge_modelstringrequired
amount_penceintegerrequired
currencystringrequired
return_pathstringrequired
return_urlstringrequired
curl -X POST "https://www.membber.com/api/v1/drops/gift-cards/public/<handle>/purchase" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_pence": 1500,
    "idempotency_key": "1f0e2d3c-4b5a-4678-9abc-def012345678",
    "purchaser": {
      "name": "Example name",
      "email": "alex@example.com"
    }
  }'
Response, 200
{
  "gift_card_id": "5c203c9b-0000-4000-8000-d0c50000005c",
  "client_secret": "<client_secret>",
  "publishable_key": "<publishable_key>",
  "connected_account_id": "231b6f63-0000-4000-8000-d0c500000023",
  "charge_model": "<charge_model>",
  "amount_pence": 1500,
  "currency": "GBP",
  "return_path": "<return_path>",
  "return_url": "https://example.com/image.jpg"
}

Whether a gift card purchase has been paid.

PUBLIC. What the shop page shows after Stripe returns the buyer. Never the code.

auth none, publicop getDropsGiftCardPurchaserate limitederrors

Parameters

handlestringpathrequired
giftCardIdstringpathrequired

Response, 200

gift_card_idstring · uuidrequired
paidbooleanrequired
amount_penceintegerrequired
currencystringrequired
sent_tostringrequired
for_someone_elsebooleanrequired
recipient_namestring, nullablerequired
expires_atstring, nullablerequired
shop_namestringrequired
curl -G "https://www.membber.com/api/v1/drops/gift-cards/public/<handle>/purchase/ed89b19b-0000-4000-8000-d0c5000000ed"
Response, 200
{
  "gift_card_id": "5c203c9b-0000-4000-8000-d0c50000005c",
  "paid": true,
  "amount_pence": 1500,
  "currency": "GBP",
  "sent_to": "<sent_to>",
  "for_someone_else": true,
  "recipient_name": "<recipient_name>",
  "expires_at": "<expires_at>",
  "shop_name": "<shop_name>"
}

Start setting up the shop from her own Instagram.

Bound to the calling login and shop. web: returns `authorize_url` and sets an httpOnly binder cookie on this response, so only this signed-in browser can finish it. app: returns a one-use `launch_url` for the sign-in sheet; the sheet comes back to membber-business://instagram-import?import=…&outcome=returned&code=… and the app finishes with POST …/{importId}/complete. The OAuth return is a server page. Refused with INSTAGRAM_NOT_AVAILABLE when the feature is off for this shop.

Request body

store_idstring · uuidrequired

The shop (business auth checks the caller runs it).

surfaceenumrequired
webapp
entryenumrequired
from_signupfrom_setupfrom_logofrom_shopfrom_claimfrom_dm_link
modeenumrequired
fulllogophotos
preview_idstring · uuid, nullableoptional

Previews "Make it mine": the preview being claimed.

force_reauthbooleanrequired

"Use a different Instagram account": Instagram asks her to log in again.

Response, 200

import_idstring · uuidrequired
launch_urlstring · uri, nullablerequired

app only (null for web): open in ASWebAuthenticationSession with scheme membber-business. Single use, 15 minutes.

authorize_urlstring · uri, nullablerequired

web only (null for app): Instagram's login URL. Open it in the same tab; this response set the binder cookie it needs.

expires_atstringrequired
curl -X POST "https://www.membber.com/api/v1/drops/instagram-import" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "surface": "web",
    "entry": "from_signup",
    "mode": "full",
    "force_reauth": true
  }'
Response, 200
{
  "import_id": "f3921ff5-0000-4000-8000-d0c5000000f3",
  "launch_url": "https://example.com",
  "authorize_url": "https://example.com",
  "expires_at": "<expires_at>"
}

Finish, or back out: wipe the fetched Instagram profile and posts now.

Called when she finishes or leaves. Otherwise they expire after 24 hours and the daily purge deletes them (within 48 hours at most).

Parameters

importIdstringpathrequired
store_idstring · uuidqueryrequired

The shop (business auth checks the caller runs it).

importIdstring · uuidqueryrequired

Response, 200

import_idstring · uuidrequired
statusenumrequired
saveddiscarded
curl -X DELETE "https://www.membber.com/api/v1/drops/instagram-import/e6d327a0-0000-4000-8000-d0c5000000e6?store_id=6659c139-0000-4000-8000-d0c500000066&importId=e6d327a0-0000-4000-8000-d0c5000000e6" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678"
Response, 200
{
  "import_id": "f3921ff5-0000-4000-8000-d0c5000000f3",
  "status": "saved"
}

Her Instagram profile and recent posts, ready to pick from.

Bound to the same Membber login AND the same shop that started it. Usable for 24 hours; the daily purge then deletes it (within 48 hours at most).

Parameters

importIdstringpathrequired
store_idstring · uuidqueryrequired

The shop (business auth checks the caller runs it).

importIdstring · uuidqueryrequired

Response, 200

importobjectrequired
16 child fields
idstring · uuidrequired
statusstringrequired

fetched | saving | saved | failed | expired | discarded | started | launched | exchanging

failure_reasonenumrequired
personal_accountcancellednot_open_yetnothing_to_importexpiredinstagram_busyfailed
surfaceenumrequired
webapp
entryenumrequired
from_signupfrom_setupfrom_logofrom_shopfrom_claimfrom_dm_link
modeenumrequired
fulllogophotos
expires_atstringrequired
accountobjectrequired
3 child fields
usernamestringrequired
namestring, nullablerequired
picture_urlstring, nullablerequired
tilesarray of objectrequired
9 child fields
keystringrequired

The piece's Instagram media id.

parent_keystring, nullablerequired

The album's id, for a piece of an album post.

kindenumrequired
imagevideo
preview_urlstring, nullablerequired

https (Instagram CDN, expiring) or a data: URL (the local fake). Null when withheld.

availabilityenumrequired

unavailable: Instagram withheld it (copyrighted or flagged), shown greyed. cover_only: a film that comes in as its cover.

okcover_onlyunavailable
caption_excerptstring, nullablerequired

For the tile's screen-reader label only. Never shown, never kept.

taken_atstring, nullablerequired
already_in_shopbooleanrequired
suggestedbooleanrequired

Pre-ticked: the best 6, at most one per post, never more than the shop has room for.

logoobjectrequired
2 child fields
offeredbooleanrequired
suggestedbooleanrequired
nameobjectrequired
2 child fields
initialstringrequired
offerstring, nullablerequired
bioobjectrequired
2 child fields
initialstringrequired
offerstring, nullablerequired
shopobjectrequired
3 child fields
namestring, nullablerequired
descriptionstring, nullablerequired
logo_urlstring, nullablerequired
carousel_roomintegerrequired
carousel_maxintegerrequired
keeparray of stringrequired
curl -G "https://www.membber.com/api/v1/drops/instagram-import/e6d327a0-0000-4000-8000-d0c5000000e6" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066" \
  --data-urlencode "importId=e6d327a0-0000-4000-8000-d0c5000000e6"
Response, 200
{
  "import": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "status": "<status>",
    "failure_reason": "personal_account",
    "surface": "web",
    "entry": "from_signup",
    "mode": "full",
    "expires_at": "<expires_at>",
    "account": {
      "username": "<username>",
      "name": "Example name",
      "picture_url": "https://example.com/image.jpg"
    },
    "tiles": [
      {
        "key": "<key>",
        "parent_key": "<parent_key>",
        "kind": "image",
        "preview_url": "https://example.com/image.jpg",
        "availability": "ok",
        "caption_excerpt": "<caption_excerpt>",
        "taken_at": "<taken_at>",
        "already_in_shop": true,
        "suggested": true
      }
    ],
    "logo": {
      "offered": true,
      "suggested": true
    },
    "name": {
      "initial": "<initial>",
      "offer": "<offer>"
    },
    "bio": {
      "initial": "<initial>",
      "offer": "<offer>"
    },
    "shop": {
      "name": "Example name",
      "description": "Added at the front desk",
      "logo_url": "https://example.com/image.jpg"
    },
    "carousel_room": 0,
    "carousel_max": -9007199254740991,
    "keep": [
      "<keep>"
    ]
  }
}

Business app: finish an import with the code its sign-in sheet was handed.

Exchanges Instagram's code on the server, reads her profile and recent posts, and drops the token. Only for the login and shop that started the import, with the code the return page saw, once, within 10 minutes. Anything else is refused with LINK_EXPIRED (410) and the import is closed.

Parameters

importIdstringpathrequired

Request body

store_idstring · uuidrequired

The shop (business auth checks the caller runs it).

codestringrequired

The `code` from membber-business://instagram-import?…&outcome=returned&code=…, unchanged.

Response, 200

import_idstring · uuidrequired
outcomeanyrequired

ok: open the picker (GET …/{importId}). Otherwise show that outcome's screen, as the web does.

curl -X POST "https://www.membber.com/api/v1/drops/instagram-import/e6d327a0-0000-4000-8000-d0c5000000e6/complete" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "code": "EXAMPLE10"
  }'
Response, 200
{
  "import_id": "f3921ff5-0000-4000-8000-d0c5000000f3",
  "outcome": "<outcome>"
}

Bring one kept photo or film into her shop.

Copies it into the shop's own storage (the same place her uploads go). Films come in SILENT; a film over 60 seconds or 15 MB, or one that cannot be made silent safely, comes in as its cover. ONE video among her shop's own pictures: a film comes in as her video only while they hold none, so the first film that fits plays and every other comes in as its cover (`film_note: one_video`). With the video space rule on, a film is counted against the shop's video space like any video she uploads: no room (`video_space_full`, which also records her request for more), five video uploads of hers unfinished (`video_uploads_unfinished`) or a count that cannot be read (`video_space_unread`) and it comes in as its cover; the import never fails over it. Idempotent per piece.

Parameters

importIdstringpathrequired
keystringpathrequired

Request body

store_idstring · uuidrequired

The shop (business auth checks the caller runs it).

Response, 200

keystringrequired
stateenumrequired
copiedalready
mediaobjectrequired
4 child fields
idstring · uuidrequired
urlstringrequired
media_typeenumrequired
imagevideo
thumbnail_urlstring, nullablerequired
filmenumrequired
silentcover_only
film_noteenumrequired

Why a film came in as its cover: Instagram withheld it, over 60 seconds, over 15 MB, it could not be made silent safely, (one_video) her shop's own pictures already hold their one video, or, with the video space rule on, (video_space_full) her shop has no room for it, (video_uploads_unfinished) five video uploads of hers are started and not finished, or (video_space_unread) her video space could not be read just then.

withheldtoo_longtoo_bigcant_silenceone_videovideo_space_fullvideo_space_unreadvideo_uploads_unfinished
curl -X POST "https://www.membber.com/api/v1/drops/instagram-import/e6d327a0-0000-4000-8000-d0c5000000e6/pieces/<key>" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066"
  }'
Response, 200
{
  "key": "<key>",
  "state": "copied",
  "media": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "url": "https://example.com/image.jpg",
    "media_type": "image",
    "thumbnail_url": "https://example.com/image.jpg"
  },
  "film": "silent",
  "film_note": "withheld"
}

Keep her choices: logo, name, bio, and which photos to bring in.

Writes the logo, name and bio at once and fixes the kept list. Refused with CAROUSEL_FULL past the shop's 10.

Parameters

importIdstringpathrequired

Request body

store_idstring · uuidrequired

The shop (business auth checks the caller runs it).

keeparray of stringrequired

Tile keys to bring in, in any order.

logobooleanrequired

Use her Instagram picture as the shop's round logo.

namestring, nullableoptional

The shop name, only if she changed it. 2 to 100 characters.

biostring, nullableoptional

The shop bio, only if she changed it. At most 2,000 characters; empty clears it.

Response, 200

import_idstring · uuidrequired
statusstringrequired
piecesarray of stringrequired

Copy each with POST …/pieces/{key}, one at a time, showing progress.

logo_urlstring, nullablerequired
logo_smallbooleanrequired

Her Instagram picture is under 200px: tell her it may look soft.

shopobjectrequired
3 child fields
namestring, nullablerequired
descriptionstring, nullablerequired
logo_urlstring, nullablerequired
curl -X POST "https://www.membber.com/api/v1/drops/instagram-import/e6d327a0-0000-4000-8000-d0c5000000e6/save" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "keep": [
      "<keep>"
    ],
    "logo": true
  }'
Response, 200
{
  "import_id": "f3921ff5-0000-4000-8000-d0c5000000f3",
  "status": "<status>",
  "pieces": [
    "<piece>"
  ],
  "logo_url": "https://example.com/image.jpg",
  "logo_small": true,
  "shop": {
    "name": "Example name",
    "description": "Added at the front desk",
    "logo_url": "https://example.com/image.jpg"
  }
}

Whether this shop can set up from Instagram right now.

Every Instagram button on web and in the Business app hides unless `available` is true.

auth merchant:readop getInstagramImportAvailabilityrate limitederrors

Parameters

store_idstring · uuidqueryrequired

The shop (business auth checks the caller runs it).

Response, 200

availablebooleanrequired
reasonenumrequired

not_configured: no Meta app yet, hide every button. not_open_yet: testers only, and this shop is not one.

not_configurednot_open_yet
carousel_roomintegerrequired

How many more photos or films the shop carousel can take (its limit is 10).

curl -G "https://www.membber.com/api/v1/drops/instagram-import/availability" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066"
Response, 200
{
  "available": true,
  "reason": "not_configured",
  "carousel_room": 0
}

App sign-in sheet only: exchange a one-use launch token for the Instagram login URL.

Public (the Business app sign-in sheet has no Membber session). Only an app import can be launched here; a web import never has a launch token. Sets an httpOnly binder cookie; the return is accepted only with the same cookie (login-CSRF protection), and an app import is then finished only by POST …/{importId}/complete under the login that started it.

auth none, publicop launchInstagramImportidempotent retryrate limitederrors

Request body

launch_tokenstringrequired

Response, 200

authorize_urlstring · urirequired
curl -X POST "https://www.membber.com/api/v1/drops/instagram-import/launch" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "launch_token": "<launch_token>"
  }'
Response, 200
{
  "authorize_url": "https://example.com"
}

Meta's Data Deletion Request callback.

Form-encoded `signed_request`. Deletes everything that came from her Instagram: the imported photos and films, the logo and bio while still the imported ones, and every record of the link. Answers { url, confirmation_code }.

auth none, publicop instagramDataDeletionidempotent retryrate limitederrors

Response, 200

urlstring · urirequired

A page she can open to see that the deletion is done.

confirmation_codestringrequired
curl -X POST "https://www.membber.com/api/v1/drops/instagram/data-deletion" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678"
Response, 200
{
  "url": "https://example.com",
  "confirmation_code": "EXAMPLE10"
}

Meta's Deauthorize callback: she removed Membber from her Instagram.

Form-encoded `signed_request`, HMAC-SHA256 with the app secret. Deletes everything that came from her Instagram, as a data-deletion request does: the imported photos and films, the logo and bio while still the imported ones, every record of the link and any unfinished import (DEAUTHORIZE_ALSO_REMOVES_IMPORTED).

auth none, publicop instagramDeauthorizeidempotent retryrate limitederrors

Response, 200

okbooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/instagram/deauthorize" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678"
Response, 200
{
  "ok": true
}

Ask a shop a question.

Holds a question from someone with no account and emails them a code. PUBLIC. Nothing reaches the merchant and no customer record is created until the code is confirmed, so an unverified address cannot put a row in her book or a message in her inbox. One exception, chosen by the founder: an address already on this shop’s drop list with no existing thread posts straight away and receives a chat key, a drop signup already handed over that address, and a brand-new conversation exposes nothing that needs proof.

auth none, publicop startDropMessageidempotent retryrate limitederrors

Parameters

handlestringpathrequired

Request body

handlestringoptional
emailstringrequired

Where the code and the shop's replies go. Required, and exactly one address, or the request is refused.

messagestringrequired
namestringrequired
phonestring, nullableoptional

Response, 200

okbooleanrequired
expires_atstringoptional
conversation_idstringoptional
chat_tokenstringoptional
curl -X POST "https://www.membber.com/api/v1/drops/message/<handle>" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alex@example.com",
    "message": "Added at the front desk",
    "name": "Example name"
  }'
Response, 200
{
  "ok": true,
  "expires_at": "<expires_at>",
  "conversation_id": "e701a257-0000-4000-8000-d0c5000000e7",
  "chat_token": "<chat_token>"
}

Read a buyer's own conversation with a shop.

Requires the chat key issued when their emailed code was accepted. Marks the shop's replies as read.

auth none, publicop readDropChatrate limitederrors

Parameters

handlestringpathrequired
handlestringqueryoptional

Supplied by the URL PATH.

tokenstringqueryrequired

The key issued when their code was accepted.

sincestring, nullablequeryoptional

ISO instant, poll for anything newer.

Response, 200

messagesarray of objectrequired
13 child fields
idstringrequired
fromenumrequired
youshop
bodystringrequired
atstringrequired
readbooleanrequired
kindenumrequired
textimagevideofilesystemcta
mediaUrlstring, nullablerequired
mediaThumbnailUrlstring, nullablerequired
mediaWidthnumber, nullablerequired
mediaHeightnumber, nullablerequired
mediaMimeTypestring, nullablerequired
mediaFileNamestring, nullablerequired
clientMessageIdstring, nullablerequired
curl -G "https://www.membber.com/api/v1/drops/message/<handle>/chat" \
  --data-urlencode "token=<token>"
Response, 200
{
  "messages": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "from": "you",
      "body": "<body>",
      "at": "<at>",
      "read": true,
      "kind": "text",
      "mediaUrl": "https://example.com/image.jpg",
      "mediaThumbnailUrl": "https://example.com/image.jpg",
      "mediaWidth": 1,
      "mediaHeight": 1,
      "mediaMimeType": "<mediaMimeType>",
      "mediaFileName": "<mediaFileName>",
      "clientMessageId": "b36863f7-0000-4000-8000-d0c5000000b3"
    }
  ]
}

Say something else, with no code and no form.

Requires the chat key. Posts into the same conversation the merchant already sees. With `client_message_id`, the send is deduped on it, exactly as the media send is, so a retry after a lost response posts once.

auth none, publicop sendDropChatidempotent retryrate limitederrors

Parameters

handlestringpathrequired

Request body

handlestringoptional

Supplied by the URL PATH.

tokenstringrequired
messagestringrequired
client_message_idstringoptional

The bubble identity. A retry with the same id returns the original message instead of posting a second copy. Optional so an older page still sends; the buyer page always sends one.

Response, 200

okbooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/message/<handle>/chat" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "<token>",
    "message": "Added at the front desk"
  }'
Response, 200
{
  "ok": true
}

Send a photo into the same conversation, from the same composer.

Sent as `multipart/form-data` with fields `file`, `token` and `client_message_id`. Requires the chat key, exactly as the text send does: the key names the conversation, so there is no id in the body for a caller to swap. Goes through the same upload path as the app: the per-shop block is checked BEFORE the bytes are buffered, only the types the bucket accepts are taken (anything else is a 415 with a readable line, never a 500), images are capped at 10MB and videos at 50MB, and the message is deduped on `client_message_id` so a double-tap posts once. Unlike the app upload this needs no complete customer profile, a drop buyer proves themselves with a code or a receipt, and the text send asks for nothing more either.

auth none, publicop sendDropChatMediaidempotent retryrate limitederrors

Parameters

handlestringpathrequired

Response, 200

okbooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/message/<handle>/chat/media" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678"
Response, 200
{
  "ok": true
}

Send a code to open the chat.

Emails a verification code for an address the page already holds, so somebody who signed up to the shop another way does not retype it to start a conversation. No question is attached: the code opens the chat and they type once inside. PUBLIC, and rate limited like the question route, because it sends mail to a caller-supplied address. An unexpired code for the same address is REUSED rather than re-sent, so reopening the sheet cannot burn the limit or leave the person reading an older email.

auth none, publicop openDropChatidempotent retryrate limitederrors

Parameters

handlestringpathrequired

Request body

handlestringoptional
emailstringrequired

Where the code and the shop's replies go. Required, and exactly one address, or the request is refused.

namestringrequired
phonestringoptional

Response, 200

okbooleanrequired
expires_atstringrequired
reusedbooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/message/<handle>/open" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alex@example.com",
    "name": "Example name"
  }'
Response, 200
{
  "ok": true,
  "expires_at": "<expires_at>",
  "reused": true
}

Confirm the code and send the question.

Checks the emailed code and, only then, creates the lightweight customer and the conversation and posts the held question into the merchant's existing Messages tab. A wrong, missing or expired code all answer identically, so the endpoint cannot be used to discover whether a request is outstanding for an address.

auth none, publicop verifyDropMessageidempotent retryrate limitederrors

Parameters

handlestringpathrequired

Request body

handlestringoptional
emailstringrequired

Where the code and the shop's replies go. Required, and exactly one address, or the request is refused.

codestringrequired

Response, 200

okbooleanrequired
conversation_idstringrequired
chat_tokenstringrequired
curl -X POST "https://www.membber.com/api/v1/drops/message/<handle>/verify" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alex@example.com",
    "code": "EXAMPLE10"
  }'
Response, 200
{
  "ok": true,
  "conversation_id": "e701a257-0000-4000-8000-d0c5000000e7",
  "chat_token": "<chat_token>"
}

Every sale, refund and dispute on this shop's drops, as a CSV file for its books.

One row per money event (Sale, Refund, Dispute, Gift card sold, Gift card refund) in the shop's own zone, drops only. Every figure is recorded, "None" where it cannot exist, or "Not recorded"; nothing is estimated. No buyer names or contact details. 422 EXPORT_TOO_LARGE past 10,000 lines. Needs the can_view_reports permission.

Parameters

store_idstring · uuidqueryrequired
periodenumqueryrequired

UK tax periods in the shop's own zone: 6 April to 5 April, or the most recent COMPLETE standard quarter (6 Apr, 6 Jul, 6 Oct, 6 Jan). "custom" needs start_date and end_date; a preset refuses them.

this_tax_yearlast_tax_yearlast_tax_quartercustom
start_datestringqueryoptional

custom only: first shop-local day, inclusive.

end_datestringqueryoptional

custom only: last shop-local day, inclusive.

include_practicebooleanqueryoptional

true brings back practice (Stripe test) card payments, each marked "Practice payment".

Response, 200

exportobjectrequired
17 child fields
filenamestringrequired
csvstringrequired

UTF-8 CSV text starting with a BOM, CRLF line endings

sha256stringrequired

Of the CSV text as UTF-8 bytes, BOM included.

row_countintegerrequired
salesintegerrequired
refundsintegerrequired
disputesintegerrequired
gift_cards_soldintegerrequired
gift_card_refundsintegerrequired
fromstringrequired

Shop-local first day actually covered.

tostringrequired

Shop-local last day actually covered (never after today).

timezonestringrequired

The IANA zone every date and time in the file is in.

as_ofstring · date-timerequired

When the request started; nothing after it is in the file.

include_practicebooleanrequired
left_outobjectrequired
2 child fields
practice_paymentsintegerrequired

Practice card payments and practice gift cards left out.

unpaid_ordersintegerrequired

Drop orders placed in the period and not paid yet. Never rows.

undated_refundsintegerrequired

Refund rows whose day is not recorded; the file says so on each.

mismatched_ordersintegerrequired

Orders whose refund records do not match; those rows say so.

curl -G "https://www.membber.com/api/v1/drops/money/export" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066" \
  --data-urlencode "period=this_tax_year"
Response, 200
{
  "export": {
    "filename": "<filename>",
    "csv": "<csv>",
    "sha256": "<sha256>",
    "row_count": 1,
    "sales": 0,
    "refunds": 0,
    "disputes": 0,
    "gift_cards_sold": 0,
    "gift_card_refunds": 0,
    "from": "<from>",
    "to": "<to>",
    "timezone": "Europe/London",
    "as_of": "2026-07-01T09:00:00Z",
    "include_practice": true,
    "left_out": {
      "practice_payments": 0,
      "unpaid_orders": 0
    },
    "undated_refunds": 0,
    "mismatched_orders": 0
  }
}

Place a drop order to pay by bank transfer or cash.

Places an unpaid drop order priced exactly like a card order. The server checks the method is one the shop takes for this order, writes the order at awaiting_payment with the payee details and, for a bank transfer, the deadline, converts the basket's holds so the boxes are the buyer's from now, and returns the order page link. Public: the buyer has no account.

auth none, publicop placeDropOrderidempotent retryrate limitederrors

Request body

store_codestringrequired
idempotency_keystringrequired
batch_drop_idstring · uuidrequired
cart_tokenstringrequired
slot_idstring · uuid, nullableoptional
order_typestringrequired
collection_modestringrequired
itemsarray of objectrequired
4 child fields
menu_item_idstring · uuidrequired
drop_line_idstring · uuidoptional

The drop line bought (the public drop's items[].id). Needed for an item sold in two or more sizes or colours; left out, the item's one line.

quantityintegerrequired
shown_price_penceinteger, nullableoptional

The price for one the buyer was shown, before the fee (the drop's items[].price_pence). Any difference from the current price is refused with 409 PRICE_CHANGED and the current prices.

customerobjectrequired
3 child fields
namestringrequired
phonestring, nullableoptional
emailstring, nullableoptional

The buyer's email. Optional: leave it out, or send null or an empty string, for no email. Anything else must be exactly one address, or the request is refused.

device_fingerprintstring, nullableoptional
gift_card_codestring, nullableoptional
promo_codestring, nullableoptional
payment_methodenumrequired

How the buyer will pay: a bank transfer before the deadline, or cash when they collect.

bank_transfercash

Response, 200

order_idstringrequired
order_numberstringrequired
statusstringrequired
requires_paymentbooleanrequired
payment_methodenumrequired
bank_transfercash
order_page_pathstringrequired
payment_due_atstring, nullablerequired
collection_codestring, nullablerequired
amount_pencenumberrequired
currencystringrequired
merchant_namestringrequired
replayedbooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/order" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_code": "EXAMPLE10",
    "idempotency_key": "1f0e2d3c-4b5a-4678-9abc-def012345678",
    "batch_drop_id": "63a4cc66-0000-4000-8000-d0c500000063",
    "cart_token": "<cart_token>",
    "order_type": "<order_type>",
    "collection_mode": "<collection_mode>",
    "items": [
      {
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "quantity": 1
      }
    ],
    "customer": {
      "name": "Example name"
    },
    "payment_method": "bank_transfer"
  }'
Response, 200
{
  "order_id": "4991ffac-0000-4000-8000-d0c500000049",
  "order_number": "<order_number>",
  "status": "<status>",
  "requires_payment": true,
  "payment_method": "bank_transfer",
  "order_page_path": "<order_page_path>",
  "payment_due_at": "<payment_due_at>",
  "collection_code": "EXAMPLE10",
  "amount_pence": 1500,
  "currency": "GBP",
  "merchant_name": "<merchant_name>",
  "replayed": true
}

The order page for an order placed to pay by bank transfer or cash.

Everything the order page renders, from the placed row: the state line, the payee details snapshotted at placement, the deadline, the collection code once it is the buyer's, the items and the shop. Addressed by order number and the order page token; a wrong token is a 404. PUBLIC: a pay-later buyer has no account and no PaymentIntent.

auth none, publicop getDropOrderPagerate limitederrors

Parameters

order_numberstringqueryrequired
tstringqueryrequired

The order page token from the placement, the text or the email.

Response, 200

order_numberstringrequired
stateenumrequired
bank_awaitingbank_overduebank_paidcash_awaitingcard_paidcollectedreleasednot_collectedrefundedcancelled
payment_methodenumrequired
pay_nowbank_transfercash
total_pencenumberrequired
buyer_fee_pencenumberrequired
refunded_pencenumberrequired
promo_codestring, nullableoptional
promo_discount_pencenumberoptional
currencystringrequired
collection_codestring, nullablerequired
collect_scan_tokenstring, nullablerequired
payment_due_atstring, nullablerequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
payeeobjectrequired
5 child fields
account_namestringrequired
sort_codestringrequired
account_numberstringrequired
referencestringrequired
amount_pencenumberrequired
itemsarray of objectrequired
3 child fields
namestringrequired
quantitynumberrequired
line_total_pencenumberrequired
collectionobjectrequired
5 child fields
datestring, nullablerequired
starts_atstring, nullablerequired
ends_atstring, nullablerequired
timezonestringrequired
addressstring, nullablerequired
shopobjectrequired
7 child fields
namestringrequired
codestringrequired
handlestring, nullablerequired
primary_colorstring, nullablerequired
logo_urlstring, nullablerequired
contact_emailstring, nullablerequired
contact_phonestring, nullablerequired
dropobjectrequired
2 child fields
codestringrequired
namestringrequired
buyerobjectrequired
3 child fields
first_namestring, nullablerequired
phone_maskedstring, nullablerequired
email_maskedstring, nullablerequired
allergen_linestring, nullablerequired
curl -G "https://www.membber.com/api/v1/drops/order-page" \
  --data-urlencode "order_number=<order_number>" \
  --data-urlencode "t=<t>"
Response, 200
{
  "order_number": "<order_number>",
  "state": "bank_awaiting",
  "payment_method": "pay_now",
  "total_pence": 1500,
  "buyer_fee_pence": 1500,
  "refunded_pence": 1500,
  "promo_code": "EXAMPLE10",
  "promo_discount_pence": 1500,
  "currency": "GBP",
  "collection_code": "EXAMPLE10",
  "collect_scan_token": "<collect_scan_token>",
  "payment_due_at": "<payment_due_at>",
  "paid_at": "<paid_at>",
  "collected_at": "<collected_at>",
  "payee": {
    "account_name": "<account_name>",
    "sort_code": "EXAMPLE10",
    "account_number": "<account_number>",
    "reference": "<reference>",
    "amount_pence": 1500
  },
  "items": [
    {
      "name": "Example name",
      "quantity": 1,
      "line_total_pence": 1500
    }
  ],
  "collection": {
    "date": "<date>",
    "starts_at": "<starts_at>",
    "ends_at": "<ends_at>",
    "timezone": "Europe/London",
    "address": "<address>"
  },
  "shop": {
    "name": "Example name",
    "code": "EXAMPLE10",
    "handle": "<handle>",
    "primary_color": "<primary_color>",
    "logo_url": "https://example.com/image.jpg",
    "contact_email": "alex@example.com",
    "contact_phone": "+44 7700 900123"
  },
  "drop": {
    "code": "EXAMPLE10",
    "name": "Example name"
  },
  "buyer": {
    "first_name": "Alex",
    "phone_masked": "+44 7700 900123",
    "email_masked": "alex@example.com"
  },
  "allergen_line": "<allergen_line>"
}

Add a mobile to an order bought in one tap.

Writes a UK mobile onto a paid order that has none, so the shop can reach the buyer about collection. Authorised by the PaymentIntent client_secret from the return URL. Only ever fills a blank: an order that already carries a number is left untouched and answers success.

auth none, publicop addDropOrderPhoneidempotent retryrate limitederrors

Request body

client_secretstringoptional
order_numberstringoptional
order_tokenstringoptional
phonestringrequired

Response, 200

okbooleanrequired
savedbooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/order-phone" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+44 7700 900123"
  }'
Response, 200
{
  "ok": true,
  "saved": true
}

Start a payment for a drop order.

Creates the PaymentIntent for an anonymous drop purchase and returns its client secret, so the card is taken on the shop's own page rather than on a hosted Stripe page. PUBLIC: the buyer has no account. Deliberately the SAME service call the customer app uses, so stock banking, receipts and the sold-out auto-refund all keep hanging off payment_intent.succeeded.

auth none, publicop startDropPaymentidempotent retryrate limitederrors

Request body

store_codestringrequired
idempotency_keystringrequired
batch_drop_idstring · uuidrequired
cart_tokenstringrequired
slot_idstring · uuid, nullableoptional
order_typestringrequired
collection_modestringrequired
itemsarray of objectrequired
4 child fields
menu_item_idstring · uuidrequired
drop_line_idstring · uuidoptional

The drop line bought (the public drop's items[].id). Needed for an item sold in two or more sizes or colours; left out, the item's one line.

quantityintegerrequired
shown_price_penceinteger, nullableoptional

The price for one the buyer was shown, before the fee (the drop's items[].price_pence). Any difference from the current price is refused with 409 PRICE_CHANGED and the current prices.

express_walletbooleanoptional
customerobjectrequired
3 child fields
namestringrequired
phonestring, nullableoptional
emailstring, nullableoptional

The buyer's email. Optional: leave it out, or send null or an empty string, for no email. Anything else must be exactly one address, or the request is refused.

device_fingerprintstring, nullableoptional
gift_card_codestring, nullableoptional
promo_codestring, nullableoptional

Response, 200

order_idstringrequired
order_numberstringrequired
statusstringrequired
requires_paymentbooleanrequired
client_secretstring, nullablerequired
connected_account_idstringrequired
publishable_keystring, nullablerequired
charge_modelenumrequired
destinationdirect
amount_pencenumberrequired
currencystringrequired
merchant_namestringrequired
order_page_pathstring, nullableoptional
curl -X POST "https://www.membber.com/api/v1/drops/payment" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_code": "EXAMPLE10",
    "idempotency_key": "1f0e2d3c-4b5a-4678-9abc-def012345678",
    "batch_drop_id": "63a4cc66-0000-4000-8000-d0c500000063",
    "cart_token": "<cart_token>",
    "order_type": "<order_type>",
    "collection_mode": "<collection_mode>",
    "items": [
      {
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "quantity": 1
      }
    ],
    "customer": {
      "name": "Example name"
    }
  }'
Response, 200
{
  "order_id": "4991ffac-0000-4000-8000-d0c500000049",
  "order_number": "<order_number>",
  "status": "<status>",
  "requires_payment": true,
  "client_secret": "<client_secret>",
  "connected_account_id": "231b6f63-0000-4000-8000-d0c500000023",
  "publishable_key": "<publishable_key>",
  "charge_model": "destination",
  "amount_pence": 1500,
  "currency": "GBP",
  "merchant_name": "<merchant_name>",
  "order_page_path": "<order_page_path>"
}

Previews: Make it mine, step 3. The claim.

Merchant, her own store. In one transaction: refused unless the caller is the account that verified the code (409 NOT_VERIFIED) and the preview is still unclaimed (409 PREVIEW_CLAIMED); the photos she kept are copied into her own storage and become her shop photos (and her logo, only if she has none), with the licence recorded in the consent ledger; the private originals are deleted. Never creates menu items or a drop. A retry by the same account for the same store answers the same.

Parameters

tokenstringpathrequired

Request body

store_idstring · uuidrequired

Her own shop (the one the Drops signup made, or already had).

keep_photo_idsarray of string · uuidrequired

The preview photos she ticked "keep" (her logo and product photos). Empty: start without them.

licence_versionintegeroptional

The photo licence version the page showed her. Required when keep_photo_ids is not empty.

Response, 200

claimedbooleanrequired
proof_codestringrequired
nextstringrequired
photos_keptnumberrequired
shopobjectrequired
2 child fields
namestring, nullablerequired
drops_linkstring, nullablerequired
instagram_hrefstring, nullablerequired
curl -X POST "https://www.membber.com/api/v1/drops/previews/<token>/claim/complete" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "keep_photo_ids": [
      "11a759c2-0000-4000-8000-d0c500000011"
    ]
  }'
Response, 200
{
  "claimed": true,
  "proof_code": "EXAMPLE10",
  "next": "<next>",
  "photos_kept": 1,
  "shop": {
    "name": "Example name",
    "drops_link": "<drops_link>"
  },
  "instagram_href": "<instagram_href>"
}

Previews: Make it mine, step 1. Email her a 6-digit code.

Public: the private link is the key. Sends a code valid for 15 minutes to the address; a new request supersedes the last code. Rate limited per IP, per link and per address. 404 when the link is unknown, deleted or past its 30 days; 409 PREVIEW_CLAIMED once it has been claimed.

auth none, publicop dropsPreviewClaimStartidempotent retryrate limitederrors

Parameters

tokenstringpathrequired

Request body

shop_namestringrequired

Her shop name, pre-filled from the preview and editable.

owner_namestringrequired

Her full name (the Drops signup asks the same).

emailstring · emailrequired
visitstringoptional

The visit ticket her page was given when it loaded. Opaque; one per page load.

Response, 200

sentbooleanrequired
expires_atstringrequired
resend_after_secnumberrequired
curl -X POST "https://www.membber.com/api/v1/drops/previews/<token>/claim/start" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "shop_name": "<shop_name>",
    "owner_name": "<owner_name>",
    "email": "alex@example.com"
  }'
Response, 200
{
  "sent": true,
  "expires_at": "<expires_at>",
  "resend_after_sec": 1
}

Previews: Make it mine, step 2. The code proves the inbox; a session comes back.

Public. Checks the code in constant time (5 tries, then it is locked; 15 minutes). Creates the account with the email confirmed and no password, or signs in the account that already has this address. An admin account is refused (403 USE_YOUR_LOGIN). Codes: 422 CODE_WRONG (with attempts_left), 410 CODE_EXPIRED, 423 CODE_LOCKED, 404 NO_CODE, 409 PREVIEW_CLAIMED.

auth none, publicop dropsPreviewClaimVerifyidempotent retryrate limitederrors

Parameters

tokenstringpathrequired

Request body

emailstring · emailrequired
codestringrequired

Response, 200

access_tokenstringrequired
refresh_tokenstringrequired
user_idstringrequired
new_accountbooleanrequired
existing_shopobjectrequired
3 child fields
idstringrequired
namestring, nullablerequired
dropsbooleanrequired
attributionobjectrequired
6 child fields
utm_sourcestringrequired
utm_mediumstringrequired
utm_campaignstringrequired
from_pagestringrequired
landing_pathstringrequired
methodstringrequired
curl -X POST "https://www.membber.com/api/v1/drops/previews/<token>/claim/verify" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alex@example.com",
    "code": "EXAMPLE10"
  }'
Response, 200
{
  "access_token": "<access_token>",
  "refresh_token": "<refresh_token>",
  "user_id": "f73aee0f-0000-4000-8000-d0c5000000f7",
  "new_account": true,
  "existing_shop": {
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "name": "Example name",
    "drops": true
  },
  "attribution": {
    "utm_source": "<utm_source>",
    "utm_medium": "<utm_medium>",
    "utm_campaign": "<utm_campaign>",
    "from_page": "<from_page>",
    "landing_path": "<landing_path>",
    "method": "<method>"
  }
}

Previews: the page was opened, or Make it mine was tapped.

Public, called by the private preview page itself (a server visit is not an open: Meta checks every link in a DM). opened is kept once per visit (each page load is a visit; a retry is not) and bumps the open count; make_it_mine_tapped is written once per preview and noted in her visit. `visit` is required: a post without one is a 400 and records nothing. A crawler user agent, an unknown link, an expired one and a visit ticket that is not good for the link all answer recorded: false (an open with a bad ticket is never counted).

auth none, publicop dropsPreviewPageEventidempotent retryrate limitederrors

Parameters

tokenstringpathrequired

Request body

eventenumrequired

opened: the page loaded and was on screen. make_it_mine_tapped: she tapped Make it mine.

openedmake_it_mine_tapped
visitstringrequired

The visit ticket her page was given when it loaded. Opaque; one per page load.

Response, 200

recordedbooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/previews/<token>/events" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "opened",
    "visit": "<visit>"
  }'
Response, 200
{
  "recorded": true
}

Previews: I'm interested, then her email or phone number.

Public, no login, while her setup is not open yet. With no body it records her first tap on I'm interested (later taps change nothing) and tells the founder. With `contact` it keeps the email address or phone number she typed (once; a phone number is kept in E.164), only to reply to her about her preview and setting up her shop. 400 INVALID_CONTACT when it is neither; 404 for an unknown, expired or draft link; 409 PREVIEW_CLAIMED once she has claimed it. Her contact is never returned.

auth none, publicop dropsPreviewInterestedidempotent retryrate limitederrors

Parameters

tokenstringpathrequired

Request body

contactstringoptional

Her email address or phone number, as she typed it after tapping I'm interested. Omitted on the tap itself.

visitstringoptional

The visit ticket her page was given when it loaded. Opaque; one per page load.

Response, 200

contact_givenbooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/previews/<token>/interested" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "contact": "<contact>",
    "visit": "<visit>"
  }'
Response, 200
{
  "contact_given": true
}

Previews: which part of her preview she opened.

Public, called by the private preview page itself, never by its admin or review views. Notes one part she opened (her menu, which item, the drop, More info, or Be the first to know) against her visit: each part once per visit, each item once per item per visit. No cookie, no device detail, nothing she typed; deleted with the preview, and when she claims it. `visit` is required: a post without one is a 400 and records nothing. A crawler user agent, a visit ticket that is not good for the link and an item that is not this preview's answer recorded: false; 404 for an unknown, expired, draft or claimed link.

auth none, publicop dropsPreviewLookidempotent retryrate limitederrors

Parameters

tokenstringpathrequired

Request body

partenumrequired

What she opened: menu (her menu sheet), item (an item, named by `item`), drop (the drop, from its card or page), more_info (More info under her photo), first_to_know (she tapped Be the first to know).

menuitemdropmore_infofirst_to_know
visitstringrequired

The visit ticket her page was given when it loaded. Opaque; one per page load.

itemstringoptional

Which item she opened (part item only): a reference her page was given, never the item name.

Response, 200

recordedbooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/previews/<token>/looked-at" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "part": "menu",
    "visit": "<visit>"
  }'
Response, 200
{
  "recorded": true
}

Previews: Not for me. Deletes it now and never message her again.

Public, one tap, no login. In one transaction her handle goes on the do-not-contact list and the preview is deleted; her photos then leave storage. 404 when the link is unknown or already deleted; 409 PREVIEW_CLAIMED once she has claimed it.

auth none, publicop dropsPreviewNotForMeidempotent retryrate limitederrors

Parameters

tokenstringpathrequired

Response, 200

deletedbooleanrequired
receiptstring · uuidrequired
curl -X POST "https://www.membber.com/api/v1/drops/previews/<token>/not-for-me" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{}'
Response, 200
{
  "deleted": true,
  "receipt": "40827238-0000-4000-8000-d0c500000040"
}

Previews: her optional reason, after Not for me.

Public. Records one of three reasons (not selling now, happy with DMs, use something else) on the not-for-me event, once, within 10 minutes of the delete. 404 when it is too late or the receipt is not one.

auth none, publicop dropsPreviewNotForMeReasonidempotent retryrate limitederrors

Request body

receiptstring · uuidrequired

The receipt the not-for-me call returned.

reasonenumrequired
not_selling_nowhappy_with_dmsuse_something_else

Response, 200

savedbooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/previews/not-for-me-reason" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "receipt": "40827238-0000-4000-8000-d0c500000040",
    "reason": "not_selling_now"
  }'
Response, 200
{
  "saved": true
}

See what a promo code takes off a drop basket.

Public. Prices the basket with and without the code through the same function the order is charged by, and returns the saving, the booking fee and the new total. Read-only: nothing is reserved until the order is placed. A code that does not exist, is limited to another drop, or is paused all read the same.

auth none, publicop previewDropPromoidempotent retryrate limitederrors

Request body

store_codestringrequired
batch_drop_idstring · uuidrequired
cart_tokenstringrequired
slot_idstring · uuid, nullableoptional
order_typestringrequired
collection_modestringrequired
itemsarray of objectrequired
4 child fields
menu_item_idstring · uuidrequired
drop_line_idstring · uuidoptional

The drop line bought (the public drop's items[].id). Needed for an item sold in two or more sizes or colours; left out, the item's one line.

quantityintegerrequired
shown_price_penceinteger, nullableoptional

The price for one the buyer was shown, before the fee (the drop's items[].price_pence). Any difference from the current price is refused with 409 PRICE_CHANGED and the current prices.

gift_card_codestring, nullableoptional
promo_codestringrequired
payment_methodenumrequired
pay_nowbank_transfercash
customerobjectoptional
3 child fields
namestring, nullableoptional
phonestring, nullableoptional
emailstring, nullableoptional

The buyer's email. Optional: leave it out, or send null or an empty string, for no email. Anything else must be exactly one address, or the request is refused.

Response, 200

promo_codestringrequired
promo_labelstring, nullablerequired
goods_pencenumberrequired
discount_pencenumberrequired
buyer_fee_pencenumberrequired
total_pencenumberrequired
total_before_pencenumberrequired
saving_pencenumberrequired
curl -X POST "https://www.membber.com/api/v1/drops/promo" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_code": "EXAMPLE10",
    "batch_drop_id": "63a4cc66-0000-4000-8000-d0c500000063",
    "cart_token": "<cart_token>",
    "order_type": "<order_type>",
    "collection_mode": "<collection_mode>",
    "items": [
      {
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "quantity": 1
      }
    ],
    "promo_code": "EXAMPLE10",
    "payment_method": "pay_now"
  }'
Response, 200
{
  "promo_code": "EXAMPLE10",
  "promo_label": "<promo_label>",
  "goods_pence": 1500,
  "discount_pence": 1500,
  "buyer_fee_pence": 1500,
  "total_pence": 1500,
  "total_before_pence": 1500,
  "saving_pence": 1500
}

A drop, as a buyer sees it.

Public. Everything a buyer needs to decide: what is in it, how many are left, when it opens, when to collect, and the allergens the seller declared. Returns the drop even before it opens so the page can count down; the menu itself is withheld while the seller has chosen to hide it.

auth none, publicop getPublicBatchDroprate limitederrors

Parameters

dropCodestringpathrequired
drop_codestringqueryoptional
cart_tokenstringqueryoptional

Response, 200

dropobjectrequired
21 child fields
stripeobjectrequired
3 child fields
publishable_keystring, nullablerequired
connected_account_idstring, nullablerequired
currencystringrequired
payment_methodsarray of enumrequired
pay_nowbank_transfercash
idstringrequired
codestringrequired
namestringrequired
descriptionstring, nullablerequired
image_urlstring, nullablerequired
galleryarray of objectrequired
4 child fields
urlstringrequired
typestringrequired

image | video

titlestring, nullablerequired
posterstring, nullablerequired

Still frame for a film.

header_auto_advancebooleanoptional

Present (false) when she holds her drop's header still for every buyer. Absent: it moves on by itself.

stateenumrequired
scheduledliveclosedcollectedcancelled
opens_atstringrequired
closes_atstring, nullablerequired
collection_datestringrequired
timezonestringrequired
checkout_ttl_secondsnumberrequired
max_per_customernumber, nullablerequired
buyer_fee_percentnumber, nullablerequired
bundleobjectrequired
6 child fields
price_penceintegerrequired
buyer_price_penceintegerrequired
totalinteger, nullablerequired
remaininginteger, nullablerequired
in_other_basketsinteger, nullableoptional

How many boxes are not free only because they are in other people's baskets right now. total minus remaining minus this is how many boxes have sold. Null when the shop hides counts.

sold_outbooleanrequired
itemsarray of objectrequired
30 child fields
idstringrequired
menu_item_idstringrequired
per_bundleinteger, nullablerequired
namestringrequired

The item’s name; on a line that sells a size or colour, with them: "Hoodie, M, Black".

item_namestringoptional

Her item’s own name, on a line that sells one of its sizes or colours (`name` then reads "Hoodie, M, Black").

size_labelstringoptional

The size this line sells, as her item names it. Absent: no size.

colour_labelstringoptional

The colour this line sells, as her item names it. Absent: no colour.

size_namestringoptional

Her own word for this item’s sizes ("Flavour"), when she chose one. Absent: the word for size.

colour_namestringoptional

Her own word for this item’s colours ("Grind"), when she chose one. Absent: the word for colour.

colour_photo_urlstringoptional

The photo she chose for this colour, one of the item’s own photos. Absent: none chosen.

descriptionstring, nullablerequired
price_pencenumber, nullablerequired
buyer_price_pencenumber, nullablerequired
image_urlstring, nullablerequired
mediaarray of objectoptional

Her item's photos and videos, in her order (a video may come first), for a carousel: present only when there is more than her cover to show. Absent: the item is drawn from `image_url` alone, as before. `image_url` is always a photo.

4 child fields
urlstringrequired
typestringrequired

image | video

titlestring, nullablerequired
posterstring, nullablerequired

Still frame for a film.

media_auto_advancebooleanoptional

Present (false) when she holds her item's photos and videos still. Absent: they move on by themselves.

sold_outbooleanrequired
remainingnumber, nullablerequired
in_other_basketsnumber, nullablerequired
max_per_ordernumber, nullablerequired
batch_totalnumber, nullablerequired
categorystring, nullablerequired
emojistring, nullablerequired
dietary_tagsarray of stringrequired
allergensarray of stringrequired
allergens_declared_nonebooleanrequired
may_contain_allergensarray of stringoptional
allergen_notestring, nullableoptional
ingredientsstring, nullablerequired
is_foodbooleanoptional

False for an item that is not food (clothes, flowers): no allergen line is drawn for it, and `ingredients` is what it is made of. True or absent: food.

windowsarray of objectrequired
5 child fields
idstringrequired
starts_atstringrequired
ends_atstringrequired
fullbooleanrequired
places_leftinteger, nullablerequired
storeobjectrequired
27 child fields
codestringrequired
namestringrequired
handlestring, nullablerequired

Their shop name, which is their own address: {handle}.membber.com

logo_urlstring, nullablerequired
logo_displayobjectrequired
6 child fields
modeenumrequired

fill = cover the circle; fit = the whole logo inside, on `disc`.

fillfit
xnumberrequired

The point of the logo at the circle centre, across (0..1).

ynumberrequired

The point of the logo at the circle centre, down (0..1).

scalenumberrequired

1 = the short side spans the circle.

discstringrequired

#RRGGBB behind the logo.

aspectnumberrequired

The logo file's width / height.

banner_urlstring, nullablerequired

The merchant's own banner artwork.

wordmark_urlstring, nullablerequired
texture_urlstring, nullablerequired
primary_colorstring, nullablerequired
secondary_colorstring, nullablerequired
accent_colorstring, nullablerequired
descriptionstring, nullablerequired
addressstring, nullablerequired
legal_namestring, nullablerequired

The trader as the law knows them, printed as "{name}, run by {legal_name}".

emailstring, nullablerequired

The business email the law wants on the page, shown as a mailto link.

vat_numberstring, nullablerequired

Printed as "VAT {vat_number}" only when the trader is VAT registered.

instagramstring, nullablerequired
facebookstring, nullablerequired
tiktokstring, nullablerequired
bg_modestring, nullablerequired

plain | brand | color | image

bg_valuestring, nullablerequired

#RRGGBB when color, a URL when image

ribbon_styleenumrequired
solidstripes
cta_labelstring, nullablerequired
cta_colorstring, nullablerequired
empty_titlestring, nullablerequired
empty_bodystring, nullablerequired
earns_stampsbooleanrequired

True only when a drop order here will earn a stamp at collection (loyalty_on_drops, off by default).

curl -G "https://www.membber.com/api/v1/drops/public/EXAMPLE10"
Response, 200
{
  "drop": {
    "stripe": {
      "publishable_key": "<publishable_key>",
      "connected_account_id": "231b6f63-0000-4000-8000-d0c500000023",
      "currency": "GBP"
    },
    "payment_methods": [
      "pay_now"
    ],
    "id": "00000d1b-0000-4000-8000-d0c500000000",
    "code": "EXAMPLE10",
    "name": "Example name",
    "description": "Added at the front desk",
    "image_url": "https://example.com/image.jpg",
    "gallery": [
      {
        "url": "https://example.com/image.jpg",
        "type": "<type>",
        "title": "Morning class",
        "poster": "<poster>"
      }
    ],
    "header_auto_advance": true,
    "state": "scheduled",
    "opens_at": "<opens_at>",
    "closes_at": "<closes_at>",
    "collection_date": "<collection_date>",
    "timezone": "Europe/London",
    "checkout_ttl_seconds": 1,
    "max_per_customer": 1,
    "buyer_fee_percent": 1,
    "bundle": {
      "price_pence": 1500,
      "buyer_price_pence": 1500,
      "total": -9007199254740991,
      "remaining": -9007199254740991,
      "in_other_baskets": -9007199254740991,
      "sold_out": true
    },
    "items": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
        "per_bundle": -9007199254740991,
        "name": "Example name",
        "item_name": "<item_name>",
        "size_label": "<size_label>",
        "colour_label": "<colour_label>",
        "size_name": "<size_name>",
        "colour_name": "<colour_name>",
        "colour_photo_url": "https://example.com/image.jpg",
        "description": "Added at the front desk",
        "price_pence": 1500,
        "buyer_price_pence": 1500,
        "image_url": "https://example.com/image.jpg",
        "media": [
          {
            "url": "https://example.com/image.jpg",
            "type": "<type>",
            "title": "Morning class",
            "poster": "<poster>"
          }
        ],
        "media_auto_advance": true,
        "sold_out": true,
        "remaining": 1,
        "in_other_baskets": 1,
        "max_per_order": 1,
        "batch_total": 1,
        "category": "<category>",
        "emoji": "<emoji>",
        "dietary_tags": [
          "<dietary_tag>"
        ],
        "allergens": [
          "<allergen>"
        ],
        "allergens_declared_none": true,
        "may_contain_allergens": [
          "<may_contain_allergen>"
        ],
        "allergen_note": "Added at the front desk",
        "ingredients": "<ingredients>",
        "is_food": true
      }
    ],
    "windows": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "starts_at": "<starts_at>",
        "ends_at": "<ends_at>",
        "full": true,
        "places_left": -9007199254740991
      }
    ],
    "store": {
      "code": "EXAMPLE10",
      "name": "Example name",
      "handle": "<handle>",
      "logo_url": "https://example.com/image.jpg",
      "logo_display": {
        "mode": "fill",
        "x": 0,
        "y": 0,
        "scale": 1,
        "disc": "<disc>",
        "aspect": 1
      },
      "banner_url": "https://example.com/image.jpg",
      "wordmark_url": "https://example.com/image.jpg",
      "texture_url": "https://example.com/image.jpg",
      "primary_color": "<primary_color>",
      "secondary_color": "<secondary_color>",
      "accent_color": "<accent_color>",
      "description": "Added at the front desk",
      "address": "<address>",
      "legal_name": "<legal_name>",
      "email": "alex@example.com",
      "vat_number": "<vat_number>",
      "instagram": "<instagram>",
      "facebook": "<facebook>",
      "tiktok": "<tiktok>",
      "bg_mode": "<bg_mode>",
      "bg_value": "<bg_value>",
      "ribbon_style": "solid",
      "cta_label": "<cta_label>",
      "cta_color": "<cta_color>",
      "empty_title": "<empty_title>",
      "empty_body": "<empty_body>",
      "earns_stamps": true
    }
  }
}

Hold something in your basket.

Public. Holds stock the moment it goes in the basket, not at payment, and starts the countdown. Sets the basket to the given quantity rather than adding to it, so a retry or a double tap converges instead of stacking. Send 0 to remove. Returns which limit bound if you got less than you asked for.

auth none, publicop holdBatchDropCartidempotent retryrate limitederrors

Parameters

dropCodestringpathrequired

Request body

drop_codestringoptional
cart_tokenstringrequired
item_idstring · uuidrequired
quantityintegerrequired
buyer_emailstring · email, nullableoptional
collection_slot_idstring · uuid, nullableoptional

Response, 200

grantednumberrequired
bound_byenumrequired
nonestockmax_per_ordermax_per_customerwindow_fullwindow_not_foundreleased
remainingnumber, nullablerequired
expires_atstring, nullablerequired
curl -X POST "https://www.membber.com/api/v1/drops/public/EXAMPLE10/hold" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "cart_token": "<cart_token>",
    "item_id": "7e22b9c7-0000-4000-8000-d0c50000007e",
    "quantity": 1
  }'
Response, 200
{
  "granted": 1,
  "bound_by": "none",
  "remaining": 1,
  "expires_at": "<expires_at>"
}

Give the basket back.

Public. Releases everything this basket holds, so it goes back on sale immediately rather than waiting for the countdown. Called by the drop page when it ends a basket at the end of its hold. A card payment prepared for this basket that has not gone through is cancelled first; one that has gone through, is going through or holds money is never cancelled, and then nothing is released (released is 0): its holds end with their own time.

auth none, publicop releaseBatchDropCartidempotent retryrate limitederrors

Parameters

dropCodestringpathrequired

Request body

drop_codestringrequired
cart_tokenstringrequired

Response, 200

releasednumberrequired
curl -X POST "https://www.membber.com/api/v1/drops/public/EXAMPLE10/release" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "drop_code": "EXAMPLE10",
    "cart_token": "<cart_token>"
  }'
Response, 200
{
  "released": 1
}

Be reminded about this one drop.

Sets a per-drop reminder: the day before it opens, at open, and before it closes. PUBLIC, no account needed. Does NOT join the shop's general list; a second submission re-confirms rather than duplicating.

auth none, publicop remindAboutDropidempotent retryrate limitederrors

Parameters

dropCodestringpathrequired

Request body

phonestring, nullableoptional
emailstring, nullableoptional

An email to be told on, instead of or as well as a mobile. Optional: leave it out, or send null or an empty string, for no email. Anything else must be exactly one address, or the request is refused.

consent_textstring, nullableoptional

Response, 200

okbooleanrequired
viaenumrequired
phoneemail
alreadybooleanrequired
on_listbooleanrequired
curl -X POST "https://www.membber.com/api/v1/drops/remind/EXAMPLE10" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+44 7700 900123",
    "email": "alex@example.com",
    "consent_text": "<consent_text>"
  }'
Response, 200
{
  "ok": true,
  "via": "phone",
  "already": true,
  "on_list": true
}

Leave a review for a drop you collected.

PUBLIC, authorised entirely by the emailed token. Called twice for one review by design: the star tapped in the email records the rating, then the page adds words to the SAME row. The underlying function refuses an order that was never collected and a token that does not exist, so a review cannot exist without a purchase behind it.

auth none, publicop submitDropReviewidempotent retryrate limitederrors

Request body

tokenstring · uuidrequired
ratingintegerrequired
bodystring, nullableoptional
display_modeenumrequired
nameanonymous

Response, 200

review_idstringrequired
ratingnumberrequired
has_bodybooleanrequired
itemsarray of stringrequired
curl -X POST "https://www.membber.com/api/v1/drops/review" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "0696b9f9-0000-4000-8000-d0c500000006",
    "rating": 1,
    "display_mode": "name"
  }'
Response, 200
{
  "review_id": "1d631b62-0000-4000-8000-d0c50000001d",
  "rating": 1,
  "has_body": true,
  "items": [
    "<item>"
  ]
}

Everything the Drops seller area opens with, in one read.

The shops the signed-in business user can run drops for, and for the chosen one (store_id if she may act for it, else the first): its drops (as GET /api/v1/drops), its setup and go-live list (as GET /api/ios/settings), its logo and its shop link. Requires a Business Bearer token. A shop that holds no Drops add-on is refused with ENTITLEMENT_DENIED, exactly as the drops list refuses it.

Parameters

store_idstring · uuidqueryoptional

The shop to open on (the one this browser had open last). Used only if the caller may act for it; otherwise the first shop is opened.

Response, 200

storesarray of objectrequired

Every shop she can run drops for. Empty is a real answer.

9 child fields
idstringrequired
namestring, nullablerequired
codestring, nullablerequired

The store code, which photo uploads address a store by.

timezonestringrequired

The shop's own clock. Every drop time is in this zone.

drops_onlybooleanrequired

True when the shop came in through the Drops door and sells drops only (`dropsOnlyStoreIds`, the predicate that gives it the short go-live list). The browser uses it to open on the Drops setup list and to land Log in on the seller area. It never grants anything.

logo_urlstring, nullablerequired

The shop's logo, or null until she adds one.

logo_displayobjectrequired

How her logo sits in its circle, as buyers see it (Drops shop setup P2). Null with no logo, or until it is worked out.

6 child fields
modeenumrequired

fill = cover the circle; fit = the whole logo inside, on `disc`.

fillfit
xnumberrequired

The point of the logo at the circle centre, across (0..1).

ynumberrequired

The point of the logo at the circle centre, down (0..1).

scalenumberrequired

1 = the short side spans the circle.

discstringrequired

#RRGGBB behind the logo.

aspectnumberrequired

The logo file's width / height.

is_testbooleanrequired

The shop's hidden test switch (`stores.is_test`). True draws the one TEST chip beside the mark, and nothing else on any screen talks about tests (DESIGN.md "A screen explains itself"). False when unreadable.

sellsstring, nullableoptional

What she sells: a Drops kind page slug or "something-else", her own choice else the kind page she signed up from. Null or absent: unknown, read as food. Her catalogue is called Menu for food and Items otherwise.

store_idstring, nullablerequired

The shop opened. Null only when there are no shops.

dropsobjectrequired

The opened shop's drops, exactly as GET /api/v1/drops returns them.

10 child fields
dropsarray of objectrequired
40 child fields
idstringrequired
store_idstringrequired
codestringrequired

The short code that identifies this drop.

shop_urlstring, nullablerequired

The shop's permanent page, at its own address: https://{handle}.membber.com/

image_urlstring, nullablerequired
share_urlstringrequired

The link to this specific drop.

namestringrequired
descriptionstring, nullablerequired
statusenumrequired
draftscheduledcancelled
stateenumrequired

Computed at read time. Never sent by a client.

draftscheduledliveclosedcollectedcancelled
opens_atstringrequired
closes_atstring, nullablerequired

Null only while a draft has no closing time yet. A scheduled drop always has one.

collection_datestringrequired
timezonestringrequired
checkout_ttl_secondsnumberrequired
menu_visible_before_openbooleanrequired
show_stock_countbooleanrequired
max_per_customernumber, nullablerequired
itemsarray of objectrequired
26 child fields
idstringrequired
menu_item_idstringrequired
namestringrequired

Copied from the menu item at read time, never stored twice. On a line that sells a size or colour: the item’s name with them, "Hoodie, M, Black".

item_namestringoptional

Her item’s own name, on a line that sells one of its sizes or colours (`name` then reads "Hoodie, M, Black").

size_labelstringoptional

The size this line sells, as her item names it. Absent: no size.

colour_labelstringoptional

The colour this line sells, as her item names it. Absent: no colour.

size_namestringoptional

Her own word for this item’s sizes ("Flavour"), when she chose one. Absent: the word for size.

colour_namestringoptional

Her own word for this item’s colours ("Grind"), when she chose one. Absent: the word for colour.

colour_photo_urlstringoptional

The photo she chose for this colour, one of the item’s own photos. Absent: none chosen.

price_pencenumber, nullablerequired

What one sells at in this drop, before Membber's fee: the drop's own price when it has one, else the menu price. What checkout charges.

usual_price_pencenumber, nullableoptional

Her usual (menu) price for one.

drop_price_penceinteger, nullableoptional

This drop's own price for one, or null when it sells at the usual price.

buyer_price_pencenumber, nullablerequired
image_urlstring, nullablerequired
quantitynumberrequired

How many were put up. Meaningless when is_unlimited.

remainingnumberrequired

How many are still available. Never negative. Mirrors quantity when is_unlimited.

is_unlimitedbooleanrequired
soldnumberrequired
max_per_ordernumber, nullablerequired

null = no limit.

per_bundleinteger, nullableoptional

How many of this item go in one box of a one-price drop. Null means one.

sold_out_atstring, nullablerequired

Always null for an unlimited item.

sort_ordernumberrequired
allergensarray of stringrequired
allergens_declared_nonebooleanrequired
allergens_missingbooleanrequired
is_foodbooleanoptional

False for an item that is not food (clothes, flowers): no allergen answer is asked of it. True or absent: food.

windowsarray of objectrequired
9 child fields
idstringrequired

Send it back on the windows PUT to edit this window in place.

starts_atstringrequired
ends_atstringrequired
max_ordersnumber, nullablerequired

null = no cap on orders in this window.

orders_remainingnumber, nullablerequired
booked_countintegeroptional

People still to collect from this window. Moving it needs confirm_booked_changes; removing it is refused.

booked_namesarray of stringoptional

First names of the people still to collect, oldest order first.

removablebooleanoptional

False when anyone holds a place in this window, collected or not: removing it is refused.

in_disputebooleanoptional

True when the only orders holding this window are disputed ones nobody collected. Absent otherwise.

moves_not_reachedobjectoptional

Buyers still to collect whom a moved collection time could not reach. Absent when there are none.

2 child fields
not_toldintegerrequired

How many buyers could not be reached about a moved time.

not_told_namesarray of stringoptional

Their first names, oldest order first, three at most (the most the sentence names). Shorter than not_told when one gave no name or there are more than three; absent when none gave one.

buyersarray of objectoptional
21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

cancelled_buyersarray of objectoptional

Orders refunded in full, disputed by the buyer, released unpaid or cancelled with the drop, newest first. Listed only: never in buyers and never in any count or total.

21 child fields
idstringrequired
order_numberstring, nullablerequired
namestring, nullablerequired
emailstring, nullablerequired
total_penceintegerrequired
paid_atstring, nullablerequired
collected_atstring, nullablerequired
fulfilment_statusstring, nullablerequired
collection_atstring, nullablerequired
statusstring, nullablerequired
payment_method_kindstring, nullablerequired
offline_payment_due_atstring, nullablerequired
offline_payment_recorded_atstring, nullablerequired
buyer_fee_penceinteger, nullableoptional
application_fee_penceinteger, nullableoptional
refunded_penceinteger, nullableoptional
stripe_modestring, nullableoptional
added_by_shopbooleanrequired
failure_codestring, nullableoptional

Why a cancelled order ended, e.g. released_unpaid, drop_cancelled, dispute_created or dispute_lost. Absent on buyers.

dispute_due_bystring, nullableoptional

On a disputed order: when the dispute must be answered by, from Stripe (ISO instant). Null when the order is not disputed or the deadline was not recorded.

dispute_lost_penceinteger, nullableoptional

The part of refunded_pence a lost dispute took: the buyer's bank took it back, the shop did not refund it. Null when none was lost or it was not recorded.

orders_countnumberrequired
units_soldnumberrequired
units_totalnumberrequired

Sum of the counted items only; an unlimited item adds nothing.

has_unlimitedbooleanrequired
gross_pencenumberrequired

TAKEN: money that has arrived and stayed, from every paid status (paid, ready, collected, part-refunded), net of recorded refunds. Card orders without the buyer fee; cash and transfer in full.

still_to_come_pencenumberrequired
unpaid_orders_countnumberrequired
bundle_price_pencenumber, nullableoptional
bundle_buyer_price_pencenumber, nullableoptional
bundle_totalnumber, nullableoptional

How many boxes were put up.

bundle_remainingnumber, nullableoptional

How many boxes are left. Stock, never writable.

is_bundlebooleanoptional

True when this drop sells one box at one price.

blockersarray of stringrequired
blocker_codesarray of objectoptional
2 child fields
codeenumrequired
no_itemsno_timesno_closesno_quantityno_priceno_allergenstime_before_openstime_before_closestime_off_day
namesarray of stringrequired
making_started_atstring, nullableoptional
created_atstringrequired
updated_atstringrequired
live_drop_idstring, nullablerequired
drops_livebooleanrequired
blocked_reasonenumrequired

Why a drop cannot open. Say this to the merchant, do not refuse blankly.

no_addoncollect_offno_paymentspayments_destinationunavailable
shop_liveboolean, nullablerequired

Whether the shop is live. A drop can only be scheduled on a live shop. Null when unreadable.

card_practice_onlybooleanrequired

Pay now is on but cards are not set up yet: this real shop is still on its old practice Stripe account, which takes no card. "Set up cards" makes its live account.

payment_methodsarray of enumrequired
pay_nowbank_transfercash
reachable_customersinteger, nullablerequired
page_viewsobjectrequired

Visits to this shop's public drops pages. Null only when the count could not be read.

4 child fields
shop_todayintegerrequired
shop_last_7_daysintegerrequired
drops_todayintegerrequired
drops_last_7_daysintegerrequired
downloads_enabledbooleanoptional

True while downloads are switched on: a seller screen then shows its Downloads row. Absent otherwise.

setupobjectrequired

The opened shop's setup. Null when it could not be read this time: the screens then read it themselves.

11 child fields
requirementsarray of objectrequired

The go-live list: the same rows GET /api/ios/settings shows and the go-live gate enforces.

4 child fields
keystringrequired
labelstringrequired
completebooleanrequired
requiredbooleanrequired
is_livebooleanrequired

Whether the shop is open to buyers.

can_go_livebooleanrequired

Whether every required row is done, so going live would be accepted.

listing_grace_untilstring, nullablerequired

When her two weeks' grace runs out (`stores.listing_grace_until`), as an ISO instant, past or future. Null when she has none. Read only: the server works it out, and no request can set it. The setup list says one line with it to a LIVE shop with a required row not done: "Finish these by 19 Oct to stay on Google and AI search." while it is ahead, "Finish these to be found on Google and AI search." once it has passed.

addressstring, nullablerequired

The address people collect from.

legal_namestring, nullablerequired
business_emailstring, nullablerequired
owner_namestring, nullablerequired

What signup's "Your full name" stored. Sent only to the owner herself; null for a team member.

account_emailstring, nullablerequired

The email she signs in with.

phonestring, nullablerequired

The shop phone number, as she wrote it. Null until she adds one.

phone_publicbooleanrequired

Her switch for showing that number on her shop page (`stores.phone_public`). Off unless she turned it on; only an owner may change it, through PATCH /api/ios/settings.

shop_urlstring, nullablerequired

The shop's permanent page, at its own address (https://{handle}.membber.com/). Null until a handle exists or when unreadable.

video_spacebooleanoptional

Present (true) only while the video space rule is switched on: her sheets may then ask what she has used (GET /api/v1/drops/shop/videos). Left out while it is off, and nothing is asked.

video_compressbooleanoptional

Present (true) only while shrinking videos in the seller's browser is switched on: a video she adds is then made 720p H.264 MP4 on her own device before it is sent. Left out while it is off, and the file she picked goes as it is.

curl -G "https://www.membber.com/api/v1/drops/seller-home" \
  -H "Authorization: Bearer $MEMBBER_TOKEN"
Response, 200
{
  "stores": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "name": "Example name",
      "code": "EXAMPLE10",
      "timezone": "Europe/London",
      "drops_only": true,
      "logo_url": "https://example.com/image.jpg",
      "logo_display": {
        "mode": "fill",
        "x": 0,
        "y": 0,
        "scale": 1,
        "disc": "<disc>",
        "aspect": 1
      },
      "is_test": true,
      "sells": "<sells>"
    }
  ],
  "store_id": "6659c139-0000-4000-8000-d0c500000066",
  "drops": {
    "drops": [
      {
        "id": "00000d1b-0000-4000-8000-d0c500000000",
        "store_id": "6659c139-0000-4000-8000-d0c500000066",
        "code": "EXAMPLE10",
        "shop_url": "https://example.com/image.jpg",
        "image_url": "https://example.com/image.jpg",
        "share_url": "https://example.com/image.jpg",
        "name": "Example name",
        "description": "Added at the front desk",
        "status": "draft",
        "state": "draft",
        "opens_at": "<opens_at>",
        "closes_at": "<closes_at>",
        "collection_date": "<collection_date>",
        "timezone": "Europe/London",
        "checkout_ttl_seconds": 1,
        "menu_visible_before_open": true,
        "show_stock_count": true,
        "max_per_customer": 1,
        "items": [
          {
            "id": "00000d1b-0000-4000-8000-d0c500000000",
            "menu_item_id": "ca65a6e7-0000-4000-8000-d0c5000000ca",
            "name": "Example name",
            "item_name": "<item_name>",
            "size_label": "<size_label>",
            "colour_label": "<colour_label>",
            "size_name": "<size_name>",
            "colour_name": "<colour_name>",
            "colour_photo_url": "https://example.com/image.jpg",
            "price_pence": 1500,
            "usual_price_pence": 1500,
            "drop_price_pence": 1500,
            "buyer_price_pence": 1500,
            "image_url": "https://example.com/image.jpg",
            "quantity": 1,
            "remaining": 1,
            "is_unlimited": true,
            "sold": 1,
            "max_per_order": 1,
            "per_bundle": -9007199254740991,
            "sold_out_at": "<sold_out_at>",
            "sort_order": 1,
            "allergens": [
              null
            ],
            "allergens_declared_none": true,
            "allergens_missing": true,
            "is_food": true
          }
        ],
        "windows": [
          {
            "id": "00000d1b-0000-4000-8000-d0c500000000",
            "starts_at": "<starts_at>",
            "ends_at": "<ends_at>",
            "max_orders": 1,
            "orders_remaining": 1,
            "booked_count": 1,
            "booked_names": [
              null
            ],
            "removable": true,
            "in_dispute": true
          }
        ],
        "moves_not_reached": {
          "not_told": -9007199254740991,
          "not_told_names": [
            "<not_told_name>"
          ]
        },
        "buyers": [
          {
            "id": "00000d1b-0000-4000-8000-d0c500000000",
            "order_number": "<order_number>",
            "name": "Example name",
            "email": "alex@example.com",
            "total_pence": 1500,
            "paid_at": "<paid_at>",
            "collected_at": "<collected_at>",
            "fulfilment_status": "<fulfilment_status>",
            "collection_at": "<collection_at>",
            "status": "<status>",
            "payment_method_kind": "<payment_method_kind>",
            "offline_payment_due_at": "<offline_payment_due_at>",
            "offline_payment_recorded_at": "<offline_payment_recorded_at>",
            "buyer_fee_pence": 1500,
            "application_fee_pence": 1500,
            "refunded_pence": 1500,
            "stripe_mode": "<stripe_mode>",
            "added_by_shop": true,
            "failure_code": "EXAMPLE10",
            "dispute_due_by": "<dispute_due_by>",
            "dispute_lost_pence": 1500
          }
        ],
        "cancelled_buyers": [
          {
            "id": "00000d1b-0000-4000-8000-d0c500000000",
            "order_number": "<order_number>",
            "name": "Example name",
            "email": "alex@example.com",
            "total_pence": 1500,
            "paid_at": "<paid_at>",
            "collected_at": "<collected_at>",
            "fulfilment_status": "<fulfilment_status>",
            "collection_at": "<collection_at>",
            "status": "<status>",
            "payment_method_kind": "<payment_method_kind>",
            "offline_payment_due_at": "<offline_payment_due_at>",
            "offline_payment_recorded_at": "<offline_payment_recorded_at>",
            "buyer_fee_pence": 1500,
            "application_fee_pence": 1500,
            "refunded_pence": 1500,
            "stripe_mode": "<stripe_mode>",
            "added_by_shop": true,
            "failure_code": "EXAMPLE10",
            "dispute_due_by": "<dispute_due_by>",
            "dispute_lost_pence": 1500
          }
        ],
        "orders_count": 1,
        "units_sold": 1,
        "units_total": 1,
        "has_unlimited": true,
        "gross_pence": 1500,
        "still_to_come_pence": 1500,
        "unpaid_orders_count": 1,
        "bundle_price_pence": 1500,
        "bundle_buyer_price_pence": 1500,
        "bundle_total": 1,
        "bundle_remaining": 1,
        "is_bundle": true,
        "blockers": [
          "<blocker>"
        ],
        "blocker_codes": [
          {
            "code": "no_items",
            "names": [
              null
            ]
          }
        ],
        "making_started_at": "<making_started_at>",
        "created_at": "<created_at>",
        "updated_at": "<updated_at>"
      }
    ],
    "live_drop_id": "68f0d418-0000-4000-8000-d0c500000068",
    "drops_live": true,
    "blocked_reason": "no_addon",
    "shop_live": true,
    "card_practice_only": true,
    "payment_methods": [
      "pay_now"
    ],
    "reachable_customers": -9007199254740991,
    "page_views": {
      "shop_today": -9007199254740991,
      "shop_last_7_days": -9007199254740991,
      "drops_today": -9007199254740991,
      "drops_last_7_days": -9007199254740991
    },
    "downloads_enabled": true
  },
  "setup": {
    "requirements": [
      {
        "key": "<key>",
        "label": "<label>",
        "complete": true,
        "required": true
      }
    ],
    "is_live": true,
    "can_go_live": true,
    "listing_grace_until": "<listing_grace_until>",
    "address": "<address>",
    "legal_name": "<legal_name>",
    "business_email": "alex@example.com",
    "owner_name": "<owner_name>",
    "account_email": "alex@example.com",
    "phone": "+44 7700 900123",
    "phone_public": true
  },
  "shop_url": "https://example.com/image.jpg",
  "video_space": true,
  "video_compress": true
}

Who each conversation is with, as this shop knows them.

For the shop's messages inside Buyers: for each conversation id given (at most 50, all of this shop's), the person it is with and the name and email this shop was given for them, so her buyers list, the buyer page and the chat say one name. Owner, or staff allowed to message customers.

auth merchant:readop listDropsSellerMessagePeoplerate limitederrors

Parameters

store_idstring · uuidqueryrequired
conversation_idsstringqueryrequired

The conversations on screen, comma separated, at most 50.

Response, 200

peoplearray of objectrequired

One per conversation of this shop that has a person. Another shop's conversation is simply absent.

5 child fields
conversation_idstring · uuidrequired
customer_idstring · uuidrequired
namestring, nullablerequired

The shop's own name for them, else the latest name typed at her checkout, else the first name they gave Ask the shop. Null when she was never given one. Never the name on their shared account.

emailstring, nullablerequired

Only an email this shop was given. The title when there is no name, as on her customers list.

phonestring, nullableoptional

Only a mobile this shop was given, and only when it has no name and no email for them: then it is the title. Null otherwise.

curl -G "https://www.membber.com/api/v1/drops/seller-messages/people" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066" \
  --data-urlencode "conversation_ids=<conversation_ids>"
Response, 200
{
  "people": [
    {
      "conversation_id": "e701a257-0000-4000-8000-d0c5000000e7",
      "customer_id": "96607d1c-0000-4000-8000-d0c500000096",
      "name": "Example name",
      "email": "alex@example.com",
      "phone": "+44 7700 900123"
    }
  ]
}

The shops I can run drops for.

Every store the signed-in business user may act for that holds the Membber Drops add-on, with its timezone. Requires a Business Bearer token. This only decides which shops to OFFER: every drops route still checks the add-on itself.

auth merchant:readop listDropsSellerStoresrate limitederrors

Response, 200

storesarray of objectrequired
5 child fields
idstringrequired
namestring, nullablerequired
codestring, nullablerequired

The store code, which photo uploads address a store by.

timezonestringrequired

The shop's own clock. Every drop time is in this zone.

drops_onlybooleanrequired

True when the shop came in through the Drops door and sells drops only (`dropsOnlyStoreIds`, the predicate that gives it the short go-live list). The browser uses it to open on the Drops setup list and to land Log in on the seller area. It never grants anything.

curl -G "https://www.membber.com/api/v1/drops/seller-stores" \
  -H "Authorization: Bearer $MEMBBER_TOKEN"
Response, 200
{
  "stores": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "name": "Example name",
      "code": "EXAMPLE10",
      "timezone": "Europe/London",
      "drops_only": true
    }
  ]
}

How this shop's drops page looks.

Returns the merchant's own choices for her permanent drops page, plus the two things the picker needs to offer real options rather than invented ones: her brand colour and the shop photo she has already uploaded.

Parameters

store_idstring · uuidqueryrequired

Response, 200

shopobjectrequired
20 child fields
handlestring, nullablerequired

Their permanent name in the link, once claimed.

shop_urlstring, nullablerequired

The page this styles, at its own address: https://{handle}.membber.com/

bg_modeenumrequired
plainbrandcolorimage
bg_valuestring, nullablerequired
brand_colorstring, nullablerequired
ribbon_colorstring, nullablerequired
ribbon_sourceenumrequired
logochosen
logo_urlstring, nullablerequired

Her logo, or null.

logo_displayobjectrequired
6 child fields
modeenumrequired

fill = cover the circle; fit = the whole logo inside, on `disc`.

fillfit
xnumberrequired

The point of the logo at the circle centre, across (0..1).

ynumberrequired

The point of the logo at the circle centre, down (0..1).

scalenumberrequired

1 = the short side spans the circle.

discstringrequired

#RRGGBB behind the logo.

aspectnumberrequired

The logo file's width / height.

logo_swatchesarray of stringrequired
photo_urlstring, nullablerequired
descriptionstring, nullablerequired
cta_labelstring, nullablerequired
cta_colorstring, nullablerequired
empty_titlestring, nullablerequired
empty_bodystring, nullablerequired
instagramstring, nullablerequired
facebookstring, nullablerequired
tiktokstring, nullablerequired
sellsstring, nullableoptional

Her kind of shop: a Drops kind page slug ("cookies", "streetwear", "flowers", ...) or "something-else". Her own choice when she made one, else the kind page she signed up from. Null or absent: unknown, which reads as a food shop. Streetwear, flowers and something-else are not food: her catalogue is then called Items, not Menu.

curl -G "https://www.membber.com/api/v1/drops/shop" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066"
Response, 200
{
  "shop": {
    "handle": "<handle>",
    "shop_url": "https://example.com/image.jpg",
    "bg_mode": "plain",
    "bg_value": "<bg_value>",
    "brand_color": "<brand_color>",
    "ribbon_color": "<ribbon_color>",
    "ribbon_source": "logo",
    "logo_url": "https://example.com/image.jpg",
    "logo_display": {
      "mode": "fill",
      "x": 0,
      "y": 0,
      "scale": 1,
      "disc": "<disc>",
      "aspect": 1
    },
    "logo_swatches": [
      "<logo_swatche>"
    ],
    "photo_url": "https://example.com/image.jpg",
    "description": "Added at the front desk",
    "cta_label": "<cta_label>",
    "cta_color": "<cta_color>",
    "empty_title": "<empty_title>",
    "empty_body": "<empty_body>",
    "instagram": "<instagram>",
    "facebook": "<facebook>",
    "tiktok": "<tiktok>",
    "sells": "<sells>"
  }
}

Choose how the drops page looks.

Sets the background of the shop's permanent drops page. `color` requires a #RRGGBB and `image` requires a URL; both are refused without one rather than silently falling back, because a picker that quietly ignores the choice is worse than one that says no. Also sets her ribbon colour (`ribbon_color`, which marks it as hers so a new logo never changes it) and how her logo sits in its circle (`logo_display`), and her social links (`instagram`, `facebook`, `tiktok`: a name or a link on that network's own domain, stored as one https link, refused otherwise), and what she sells (`sells`). Every field is optional; an absent one is left as it is.

Request body

store_idstring · uuidrequired
bg_modeenumoptional
plainbrandcolorimage
bg_valuestring, nullableoptional
descriptionstringoptional
cta_labelstringoptional
cta_colorstringoptional

#RRGGBB, or empty for the brand-derived colour.

empty_titlestringoptional
empty_bodystringoptional
instagramstringoptional

Her Instagram name or link. Empty clears it.

facebookstringoptional

Her Facebook page name or link. Empty clears it.

tiktokstringoptional

Her TikTok name or link. Empty clears it.

ribbon_colorstringoptional

#RRGGBB for her ribbon (and her buttons, unless she chose a button colour). Marks it as hers.

logo_displayobjectoptional

How her logo sits in its circle. Refused when the shop has no logo.

5 child fields
modeenumrequired

fill = cover the circle; fit = the whole logo inside, on `disc`.

fillfit
xnumberrequired

The point of the logo at the circle centre, across (0..1).

ynumberrequired

The point of the logo at the circle centre, down (0..1).

scalenumberrequired

1 = the short side spans the circle.

discstringoptional
sellsstringoptional

Her kind of shop, as she chose it in "You sell": a Drops kind page slug or "something-else". Anything else is refused (422).

Response, 200

shopobjectrequired
20 child fields
handlestring, nullablerequired

Their permanent name in the link, once claimed.

shop_urlstring, nullablerequired

The page this styles, at its own address: https://{handle}.membber.com/

bg_modeenumrequired
plainbrandcolorimage
bg_valuestring, nullablerequired
brand_colorstring, nullablerequired
ribbon_colorstring, nullablerequired
ribbon_sourceenumrequired
logochosen
logo_urlstring, nullablerequired

Her logo, or null.

logo_displayobjectrequired
6 child fields
modeenumrequired

fill = cover the circle; fit = the whole logo inside, on `disc`.

fillfit
xnumberrequired

The point of the logo at the circle centre, across (0..1).

ynumberrequired

The point of the logo at the circle centre, down (0..1).

scalenumberrequired

1 = the short side spans the circle.

discstringrequired

#RRGGBB behind the logo.

aspectnumberrequired

The logo file's width / height.

logo_swatchesarray of stringrequired
photo_urlstring, nullablerequired
descriptionstring, nullablerequired
cta_labelstring, nullablerequired
cta_colorstring, nullablerequired
empty_titlestring, nullablerequired
empty_bodystring, nullablerequired
instagramstring, nullablerequired
facebookstring, nullablerequired
tiktokstring, nullablerequired
sellsstring, nullableoptional

Her kind of shop: a Drops kind page slug ("cookies", "streetwear", "flowers", ...) or "something-else". Her own choice when she made one, else the kind page she signed up from. Null or absent: unknown, which reads as a food shop. Streetwear, flowers and something-else are not food: her catalogue is then called Items, not Menu.

curl -X PUT "https://www.membber.com/api/v1/drops/shop" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066"
  }'
Response, 200
{
  "shop": {
    "handle": "<handle>",
    "shop_url": "https://example.com/image.jpg",
    "bg_mode": "plain",
    "bg_value": "<bg_value>",
    "brand_color": "<brand_color>",
    "ribbon_color": "<ribbon_color>",
    "ribbon_source": "logo",
    "logo_url": "https://example.com/image.jpg",
    "logo_display": {
      "mode": "fill",
      "x": 0,
      "y": 0,
      "scale": 1,
      "disc": "<disc>",
      "aspect": 1
    },
    "logo_swatches": [
      "<logo_swatche>"
    ],
    "photo_url": "https://example.com/image.jpg",
    "description": "Added at the front desk",
    "cta_label": "<cta_label>",
    "cta_color": "<cta_color>",
    "empty_title": "<empty_title>",
    "empty_body": "<empty_body>",
    "instagram": "<instagram>",
    "facebook": "<facebook>",
    "tiktok": "<tiktok>",
    "sells": "<sells>"
  }
}

Whether this shop runs loyalty.

Reads the shop's loyalty on/off switch, the same column the Business app's Loyalty switch reads.

Parameters

store_idstring · uuidqueryrequired

Response, 200

loyalty_enabledbooleanrequired

Whether the shop runs loyalty (stamps on orders, the stamp card).

changedbooleanrequired

On a write: false when the switch already said this and nothing was done. Always false on a read.

curl -G "https://www.membber.com/api/v1/drops/shop/loyalty" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  --data-urlencode "store_id=6659c139-0000-4000-8000-d0c500000066"
Response, 200
{
  "loyalty_enabled": true,
  "changed": true
}

Turn this shop's loyalty on or off.

The same switch as the Business app's Loyalty switch, through the same server path. On: stamps start again and stamp cards come back to customers' Wallets. Off: stamps stop, rewards already earned stay valid until they expire, stamp cards are taken out of customers' Wallets, and the customers holding a reward are told. Setting the state it already has does nothing (`changed: false`). Needs a signed-in owner or a staff member allowed to change settings; an API key is refused.

Request body

store_idstring · uuidrequired
loyalty_enabledbooleanrequired

true turns loyalty on, false turns it off.

Response, 200

loyalty_enabledbooleanrequired

Whether the shop runs loyalty (stamps on orders, the stamp card).

changedbooleanrequired

On a write: false when the switch already said this and nothing was done. Always false on a read.

curl -X PUT "https://www.membber.com/api/v1/drops/shop/loyalty" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "6659c139-0000-4000-8000-d0c500000066",
    "loyalty_enabled": true
  }'
Response, 200
{
  "loyalty_enabled": true,
  "changed": true
}

The photos and films on this shop's drops page, or on one drop's.

Everything in the carousel, in the order the merchant put it. Pass `drop_id` for ONE drop's own pictures; omit it for the shop's own page. The two are separate carousels sharing one table.

Parameters

drop_idstring · uuid, nullablequeryoptional

The drop whose own photos these are. Omitted means the shop's own page.

Response, 200

mediaarray of objectrequired
7 child fields
idstringrequired
urlstringrequired
media_typeenumrequired
imagevideo
titlestring, nullablerequired
thumbnail_urlstring, nullablerequired
sort_ordernumberrequired
is_publishedbooleanrequired
auto_advancebooleanoptional

One drop's pictures only (`drop_id` given): false when the merchant holds its header still for every buyer, true (the default) when it moves on by itself. Set with `header_auto_advance` on PATCH /api/v1/drops/{dropId}.

curl -G "https://www.membber.com/api/v1/drops/shop/media" \
  -H "Authorization: Bearer $MEMBBER_TOKEN"
Response, 200
{
  "media": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "url": "https://example.com/image.jpg",
      "media_type": "image",
      "title": "Morning class",
      "thumbnail_url": "https://example.com/image.jpg",
      "sort_order": 1,
      "is_published": true
    }
  ],
  "auto_advance": true
}

Add a photo or film to the shop's carousel.

Records an ALREADY-UPLOADED file against the shop. The file itself goes up through the existing store-image uploader, so there is one uploader rather than two. New pieces are appended, never inserted at the front. Refused with CAROUSEL_FULL (409) once that page holds 10 pieces, the most it can hold: the shop's own page holds ten, and each drop ten of its own. The refusal's details name the page that is full: `place` is `shop` or `drop`. Up to THREE videos a page, the shop's own or one drop's (since 22 Oct 2026; one before): a video is refused with VIDEOS_FULL (409) when that page already holds three, whoever brought them in; the videos a page already holds are never removed by this rule. A drop's own photo (`image_url`) sent as one of its pieces goes in front of them.

Request body

urlstringrequired
media_typeenumrequired
imagevideo
titlestring, nullableoptional
thumbnail_urlstring, nullableoptional
drop_idstring · uuid, nullableoptional

The drop whose own photos these are. Omitted means the shop's own page.

Response, 200

mediaarray of objectrequired
7 child fields
idstringrequired
urlstringrequired
media_typeenumrequired
imagevideo
titlestring, nullablerequired
thumbnail_urlstring, nullablerequired
sort_ordernumberrequired
is_publishedbooleanrequired
auto_advancebooleanoptional

One drop's pictures only (`drop_id` given): false when the merchant holds its header still for every buyer, true (the default) when it moves on by itself. Set with `header_auto_advance` on PATCH /api/v1/drops/{dropId}.

curl -X POST "https://www.membber.com/api/v1/drops/shop/media" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/image.jpg",
    "media_type": "image"
  }'
Response, 200
{
  "media": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "url": "https://example.com/image.jpg",
      "media_type": "image",
      "title": "Morning class",
      "thumbnail_url": "https://example.com/image.jpg",
      "sort_order": 1,
      "is_published": true
    }
  ],
  "auto_advance": true
}

Remove a photo or film from the shop's carousel.

Scoped to the calling store, so one shop can never remove another's media. The file leaves storage too (with its sizes), unless something else still shows it.

Parameters

mediaIdstringpathrequired
mediaIdstring · uuidqueryrequired
drop_idstring · uuid, nullablequeryoptional

The drop whose own photos these are. Omitted means the shop's own page.

Response, 200

mediaarray of objectrequired
7 child fields
idstringrequired
urlstringrequired
media_typeenumrequired
imagevideo
titlestring, nullablerequired
thumbnail_urlstring, nullablerequired
sort_ordernumberrequired
is_publishedbooleanrequired
auto_advancebooleanoptional

One drop's pictures only (`drop_id` given): false when the merchant holds its header still for every buyer, true (the default) when it moves on by itself. Set with `header_auto_advance` on PATCH /api/v1/drops/{dropId}.

curl -X DELETE "https://www.membber.com/api/v1/drops/shop/media/3813101f-0000-4000-8000-d0c500000038?mediaId=3813101f-0000-4000-8000-d0c500000038" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678"
Response, 200
{
  "media": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "url": "https://example.com/image.jpg",
      "media_type": "image",
      "title": "Morning class",
      "thumbnail_url": "https://example.com/image.jpg",
      "sort_order": 1,
      "is_published": true
    }
  ],
  "auto_advance": true
}

Replace one photo on the shop's carousel (or a drop's) in place.

Keeps the piece's place in the running order. A film replaced by a photo becomes a photo. The old file leaves storage unless something else still shows it. Scoped to the calling store.

Parameters

mediaIdstringpathrequired

Request body

mediaIdstring · uuidoptional

Supplied by the URL PATH.

urlstringrequired

The new photo, already uploaded through the store-image uploader.

drop_idstring · uuid, nullableoptional

The drop whose own photos these are. Omitted means the shop's own page.

Response, 200

mediaarray of objectrequired
7 child fields
idstringrequired
urlstringrequired
media_typeenumrequired
imagevideo
titlestring, nullablerequired
thumbnail_urlstring, nullablerequired
sort_ordernumberrequired
is_publishedbooleanrequired
auto_advancebooleanoptional

One drop's pictures only (`drop_id` given): false when the merchant holds its header still for every buyer, true (the default) when it moves on by itself. Set with `header_auto_advance` on PATCH /api/v1/drops/{dropId}.

curl -X PATCH "https://www.membber.com/api/v1/drops/shop/media/3813101f-0000-4000-8000-d0c500000038" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/image.jpg"
  }'
Response, 200
{
  "media": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "url": "https://example.com/image.jpg",
      "media_type": "image",
      "title": "Morning class",
      "thumbnail_url": "https://example.com/image.jpg",
      "sort_order": 1,
      "is_published": true
    }
  ],
  "auto_advance": true
}

Reorder the shop's carousel.

Every id is checked against this store before anything moves.

Request body

idsarray of string · uuidrequired
drop_idstring · uuid, nullableoptional

The drop whose own photos these are. Omitted means the shop's own page.

Response, 200

mediaarray of objectrequired
7 child fields
idstringrequired
urlstringrequired
media_typeenumrequired
imagevideo
titlestring, nullablerequired
thumbnail_urlstring, nullablerequired
sort_ordernumberrequired
is_publishedbooleanrequired
auto_advancebooleanoptional

One drop's pictures only (`drop_id` given): false when the merchant holds its header still for every buyer, true (the default) when it moves on by itself. Set with `header_auto_advance` on PATCH /api/v1/drops/{dropId}.

curl -X PUT "https://www.membber.com/api/v1/drops/shop/media/order" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": [
      "00000d1b-0000-4000-8000-d0c500000000"
    ]
  }'
Response, 200
{
  "media": [
    {
      "id": "00000d1b-0000-4000-8000-d0c500000000",
      "url": "https://example.com/image.jpg",
      "media_type": "image",
      "title": "Morning class",
      "thumbnail_url": "https://example.com/image.jpg",
      "sort_order": 1,
      "is_published": true
    }
  ],
  "auto_advance": true
}

Remove a stored video that nothing uses.

Deletes one of the calling shop's own stored videos that no item, drop or shop page of hers shows, and answers with her list as it now is. A video that IS on an item, a drop or her shop page is removed there, through that place's own route, never here: this answers 409 VIDEO_IN_USE for one. 404 for a name that is not one of her own files, and while the video space rule is switched off.

Parameters

namestringqueryrequired

The video's `name`, as the list gave it.

Response, 200

kindenumrequired
shop_filmsold_video
used_bytesintegerrequired

What her stored videos take, and any upload still on its way.

allowance_bytesintegerrequired

What she may store before more needs approval.

videosarray of objectrequired

Each stored video, biggest first. Null when the list was not asked for.

4 child fields
namestringrequired

The file as storage knows it (its name inside the shop video bucket). Hand it back to remove an unused one.

urlstringrequired

Its public address.

bytesinteger, nullablerequired

Its size as storage recorded it. Null when storage recorded none.

placesarray of anyrequired

Every place of hers that shows it. Empty: nothing of hers uses it (uploaded and never added, or left on a removed item).

curl -X DELETE "https://www.membber.com/api/v1/drops/shop/videos?name=Example%20name" \
  -H "Authorization: Bearer $MEMBBER_TOKEN" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678"
Response, 200
{
  "kind": "shop_film",
  "used_bytes": 0,
  "allowance_bytes": 0,
  "videos": [
    {
      "name": "Example name",
      "url": "https://example.com/image.jpg",
      "bytes": 0,
      "places": [
        "<place>"
      ]
    }
  ]
}

What the shop's videos take of its allowance, and each stored video.

The calling shop's own video space: used and allowance, in bytes, for its shop films (on items, among its shop pictures, on a drop, from Instagram) or, with `kind=sold_video`, the videos it sells as downloads. With `list=true` (shop films only) each stored video too, biggest first, with every place of hers that shows it; a video nothing of hers shows has no places. Only ever the calling shop's own files. 404 while the video space rule is switched off. 503 "Try again in a moment." when the count cannot be read: a number is never guessed.

Parameters

kindenumqueryoptional

Which allowance: her shop films (the default) or the videos she sells as downloads.

shop_filmsold_video
listenumqueryoptional

`true` to also list each stored video. Shop films only; left out, only the two numbers are read.

truefalse

Response, 200

kindenumrequired
shop_filmsold_video
used_bytesintegerrequired

What her stored videos take, and any upload still on its way.

allowance_bytesintegerrequired

What she may store before more needs approval.

videosarray of objectrequired

Each stored video, biggest first. Null when the list was not asked for.

4 child fields
namestringrequired

The file as storage knows it (its name inside the shop video bucket). Hand it back to remove an unused one.

urlstringrequired

Its public address.

bytesinteger, nullablerequired

Its size as storage recorded it. Null when storage recorded none.

placesarray of anyrequired

Every place of hers that shows it. Empty: nothing of hers uses it (uploaded and never added, or left on a removed item).

curl -G "https://www.membber.com/api/v1/drops/shop/videos" \
  -H "Authorization: Bearer $MEMBBER_TOKEN"
Response, 200
{
  "kind": "shop_film",
  "used_bytes": 0,
  "allowance_bytes": 0,
  "videos": [
    {
      "name": "Example name",
      "url": "https://example.com/image.jpg",
      "bytes": 0,
      "places": [
        "<place>"
      ]
    }
  ]
}

Be told when this shop opens a drop.

Adds a phone number or email to a shop's drop list. PUBLIC, the person has no account and does not need one. A second submission re-subscribes rather than duplicating.

auth none, publicop subscribeToDropsidempotent retryrate limitederrors

Parameters

handlestringpathrequired

Request body

handlestringoptional
phonestring, nullableoptional
emailstring, nullableoptional

An email to be told on, instead of or as well as a mobile. Optional: leave it out, or send null or an empty string, for no email. Anything else must be exactly one address, or the request is refused.

consent_textstring, nullableoptional

Response, 200

okbooleanrequired
viaenumrequired
phoneemail
alreadybooleanrequired
welcomeenumrequired
textemailnone
curl -X POST "https://www.membber.com/api/v1/drops/subscribe/<handle>" \
  -H "Idempotency-Key: 1f0e2d3c-4b5a-4678-9abc-def012345678" \
  -H "Content-Type: application/json" \
  -d '{
    "handle": "<handle>",
    "phone": "+44 7700 900123",
    "email": "alex@example.com",
    "consent_text": "<consent_text>"
  }'
Response, 200
{
  "ok": true,
  "via": "phone",
  "already": true,
  "welcome": "text"
}
WhatsApp
Book a Call
Start Free