Skip to content
Rigel docs
Open Rigel

Record a trip from a receipt or an order

shopping.trip.record

POST/api/v1/households/{household_id}/commands/shopping.trip.record

  • Command
  • Rigel runs it
  • Applies offline

Records what a receipt or order email says was bought. Give trip_id to add the receipt to a trip already saved (usually one just finished): lines for products the trip already has fill in that item's price and brand (and its amount, if it had none), and other lines are added to the trip. Without trip_id, a new trip is made; shopped_on is then required. Name the store by store_id, or by store_name to use the existing store with that name (ignoring case) or make a new one (an online one, for an order); a trip that already has a store keeps it. total_minor, currency and receipt_file_id are set when given. currency is required with total_minor or any price_minor. For an order, give order_number: without trip_id, the trip at that store with that order number is used when there is one, so the order's confirmation, shipping and delivery emails all land on one trip (items may then be empty); fails with order_not_found when there's none and no shopped_on to start one. Give arrived_on when it arrived and email_message_id for the email it came from. Newly added lines are put away in inventory a moment later, like a finished trip's items; an order's are put away once it has arrived, all at once. Any item still on the list for those products is taken off it.

Parameters#

Record a trip from a receipt or an order: parameters
FieldDescription
household_idpathstringrequired

The household's ID.

Idempotency-Keyheaderstringoptional

The command's ID. Reuse it when retrying the same command.

Request body#

JSON, as application/json.

Record a trip from a receipt or an order: request body fields
FieldDescription
trip_idstringoptional

A trip already recorded (usually one just finished), to add this receipt to. Leave out to record a new trip.

store_idstringoptional

The store, when you know its ID. Give this or store_name for a new trip.

store_namestringoptional

The store's name, when you don't have its ID: an existing store with this name (ignoring case) is used, or a new one is made.

at most 120 characters
shopped_onstring (date)optional

The day on the receipt. Required for a new trip.

itemsarray of objectsrequired

What the receipt or order says was bought. Items already on the trip get their price and brand.

at most 200 items
items[].product_idstringoptional

The product, when you know its ID. Give this or product_name.

items[].product_namestringoptional

The product's name, when you don't have its ID: an existing product with this name (ignoring case) is used, or a new one is made. Search with inventory.product.search first so you reuse the right one.

1 to 120 characters
items[].quantitynumber or nulloptional

How many, when said. Leave out when no amount was given.

greater than 0, at most 1,000,000
items[].unitone of: mg, g, kg, oz, lb, ml, l, tsp, tbsp, fl_oz, cup, pint, quart, gallon, pinch, dash, clove, slice, stick, sprig, head, bunch, stalk, can, jar, bottle, box, bag, package, carton, roll, null or nulloptional

A unit. Mass: mg, g, kg, oz, lb. Volume (US sizes): ml, l, tsp, tbsp, fl_oz, cup, pint, quart, gallon. Items, for a count of something other than the thing itself (2 cloves of garlic, 1 can of beans): pinch, dash, clove, slice, stick, sprig, head, bunch, stalk, can, jar, bottle, box, bag, package, carton, roll. Leave it out or null for a plain count of the things themselves (3 eggs).

items[].price_minorinteger or nulloptional

The line's price from the receipt, in minor units.

0 to 100,000,000
items[].brandstring or nulloptional

The brand from the receipt: "Cabot", as written, 1–120 characters.

at most 120 characters
total_minorinteger or nulloptional

The receipt's total, in minor units.

0 to 100,000,000
currencystring or nulloptional

Required with total_minor or any price_minor.

pattern ^[A-Z]{3}$
receipt_file_idstring or nulloptional

The receipt photo, already uploaded as a file.

order_numberstringoptional

For an order: the store's order number. Without trip_id, the trip at this store with this order number is used if there is one, so an order's confirmation, shipping and delivery emails all record onto one trip.

at most 80 characters
arrived_onstring (date)optional

For an order: the day it arrived, from a delivery email or a person saying so. An order's things are put away in inventory only once it has arrived.

email_message_idstringoptional

The email this was read from, so the trip links to it. Give it whenever you record from an email.

Response#

200 The command was applied.

Record a trip from a receipt or an order: response fields
FieldDescription
resultobjectrequired

What the command returned, such as the IDs it created.

result.trip_idstringrequired

The trip: the one given or found by order number, or the new one.

result.addedintegerrequired

How many lines were new to the trip.

result.updatedintegerrequired

How many lines filled in an item the trip already had.

result.removed_from_listintegerrequired

How many unchecked list items were taken off because their product was just bought.

result.put_awayobjectrequired

What happened to the newly added lines, or to all of an order's lines when this records its arrival.

result.put_away.to_put_awayintegerrequired

How many bought things Rigel will put away in inventory, a moment after the trip is saved.

result.put_away.untrackedintegerrequired

How many weren't put away because the household doesn't track them.

result.put_away.no_usual_placeintegerrequired

How many are tracked but have no usual place yet, so Rigel should ask or decide where they go.

result.put_away.on_arrivalintegerrequired

How many Rigel will put away once the order arrives (an order that hasn't arrived yet).

versionintegerrequired

The household version after the command.

Errors#

Every error has the same envelope: error.code, error.message, error.details, error.recovery and error.request_id.

Record a trip from a receipt or an order: errors
StatusCodeWhen
400validation_failedA field is missing, misspelled or out of range. details.fields lists each problem.
401unauthenticatedThe request has no valid session.
404trip_not_foundAn unknown trip is refused.
404store_not_foundAn unknown store is refused.
404file_not_foundAn unknown receipt file is refused.
404product_not_foundAn unknown product is refused.
404order_not_foundAn order number with no trip and no shopped_on is refused.
404message_not_foundAn unknown email is refused.
404not_foundThe household doesn't exist, or you aren't a member of it.
409idempotency_key_reusedThe Idempotency-Key was already used for a different command.

Examples#

These are real. Each scenario is one of the command's test cases, run on a household when this site was built, and the response is what the API returned.

A receipt with no trip makes a new unfinished trip, a store by name, and removes bought items from the list

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "store_name": "Amazon",
  "shopped_on": "2025-11-06",
  "currency": "USD",
  "total_minor": 1234,
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000A",
      "quantity": 1,
      "unit": "gallon",
      "price_minor": 499,
      "brand": "Cabot"
    },
    {
      "product_name": "Paper towels",
      "quantity": 6,
      "price_minor": 735
    }
  ]
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    store_name: "Amazon",
    shopped_on: "2025-11-06",
    currency: "USD",
    total_minor: 1234,
    items: [
      {
        product_id: "prd_01JC000000000000000000000A",
        quantity: 1,
        unit: "gallon",
        price_minor: 499,
        brand: "Cabot",
      },
      {
        product_name: "Paper towels",
        quantity: 6,
        price_minor: 735,
      },
    ],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "store_name": "Amazon",
  "shopped_on": "2025-11-06",
  "currency": "USD",
  "total_minor": 1234,
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000A",
      "quantity": 1,
      "unit": "gallon",
      "price_minor": 499,
      "brand": "Cabot"
    },
    {
      "product_name": "Paper towels",
      "quantity": 6,
      "price_minor": 735
    }
  ]
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R03DHCA6E4QN61RQD6

{
  "result": {
    "trip_id": "trp_01JC4N0000JVP6PWJ6S8P7WZ6B",
    "added": 2,
    "updated": 0,
    "removed_from_list": 1,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 2,
      "on_arrival": 0
    }
  },
  "version": 1
}

A store name matches an existing store ignoring case

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "store_name": "market BASKET",
  "shopped_on": "2025-11-06",
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000B"
    }
  ]
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    store_name: "market BASKET",
    shopped_on: "2025-11-06",
    items: [
      {
        product_id: "prd_01JC000000000000000000000B",
      },
    ],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "store_name": "market BASKET",
  "shopped_on": "2025-11-06",
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000B"
    }
  ]
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R00MX15PEYMTJBHW5A

{
  "result": {
    "trip_id": "trp_01JC4N0000MBGE5RJ6Q7V6V6T8",
    "added": 1,
    "updated": 0,
    "removed_from_list": 0,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 1,
      "on_arrival": 0
    }
  },
  "version": 1
}

A store ID sets the store

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "store_id": "sto_01JC000000000000000000000B",
  "shopped_on": "2025-11-06",
  "items": []
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    store_id: "sto_01JC000000000000000000000B",
    shopped_on: "2025-11-06",
    items: [],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "store_id": "sto_01JC000000000000000000000B",
  "shopped_on": "2025-11-06",
  "items": []
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R01524MJN522Q6YZKD

{
  "result": {
    "trip_id": "trp_01JC4N0000MBGE5RJ6Q7V6V6T8",
    "added": 0,
    "updated": 0,
    "removed_from_list": 0,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 0,
      "on_arrival": 0
    }
  },
  "version": 1
}

A receipt on a finished trip fills prices and brands, adds new lines, and takes them off the list

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "trip_id": "trp_01JC0000000000000000000001",
  "currency": "USD",
  "total_minor": 2150,
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000A",
      "quantity": 2,
      "unit": "gallon",
      "price_minor": 499,
      "brand": "Cabot"
    },
    {
      "product_id": "prd_01JC000000000000000000000B",
      "quantity": 12,
      "price_minor": 350
    },
    {
      "product_id": "prd_01JC000000000000000000000C",
      "quantity": 1,
      "price_minor": 399
    }
  ]
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    trip_id: "trp_01JC0000000000000000000001",
    currency: "USD",
    total_minor: 2150,
    items: [
      {
        product_id: "prd_01JC000000000000000000000A",
        quantity: 2,
        unit: "gallon",
        price_minor: 499,
        brand: "Cabot",
      },
      {
        product_id: "prd_01JC000000000000000000000B",
        quantity: 12,
        price_minor: 350,
      },
      {
        product_id: "prd_01JC000000000000000000000C",
        quantity: 1,
        price_minor: 399,
      },
    ],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "trip_id": "trp_01JC0000000000000000000001",
  "currency": "USD",
  "total_minor": 2150,
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000A",
      "quantity": 2,
      "unit": "gallon",
      "price_minor": 499,
      "brand": "Cabot"
    },
    {
      "product_id": "prd_01JC000000000000000000000B",
      "quantity": 12,
      "price_minor": 350
    },
    {
      "product_id": "prd_01JC000000000000000000000C",
      "quantity": 1,
      "price_minor": 399
    }
  ]
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0PD1YJS187D4MAAAG

{
  "result": {
    "trip_id": "trp_01JC0000000000000000000001",
    "added": 1,
    "updated": 2,
    "removed_from_list": 1,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 1,
      "on_arrival": 0
    }
  },
  "version": 1
}

Each trip item takes at most one line, so a second line for the product is a new item

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "trip_id": "trp_01JC0000000000000000000001",
  "currency": "USD",
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000A",
      "price_minor": 499
    },
    {
      "product_id": "prd_01JC000000000000000000000A",
      "price_minor": 520
    }
  ]
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    trip_id: "trp_01JC0000000000000000000001",
    currency: "USD",
    items: [
      {
        product_id: "prd_01JC000000000000000000000A",
        price_minor: 499,
      },
      {
        product_id: "prd_01JC000000000000000000000A",
        price_minor: 520,
      },
    ],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "trip_id": "trp_01JC0000000000000000000001",
  "currency": "USD",
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000A",
      "price_minor": 499
    },
    {
      "product_id": "prd_01JC000000000000000000000A",
      "price_minor": 520
    }
  ]
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0VY5DYP6ZDN0SGAS7

{
  "result": {
    "trip_id": "trp_01JC0000000000000000000001",
    "added": 1,
    "updated": 1,
    "removed_from_list": 0,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 1,
      "on_arrival": 0
    }
  },
  "version": 1
}

A line without a price leaves the item's price and brand alone

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "trip_id": "trp_01JC0000000000000000000001",
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000A"
    }
  ]
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    trip_id: "trp_01JC0000000000000000000001",
    items: [
      {
        product_id: "prd_01JC000000000000000000000A",
      },
    ],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "trip_id": "trp_01JC0000000000000000000001",
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000A"
    }
  ]
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0YB541GKQHJHNJXTM

{
  "result": {
    "trip_id": "trp_01JC0000000000000000000001",
    "added": 0,
    "updated": 1,
    "removed_from_list": 0,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 0,
      "on_arrival": 0
    }
  },
  "version": 1
}

The trip's store is set only when it has none

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "trip_id": "trp_01JC0000000000000000000001",
  "store_id": "sto_01JC000000000000000000000B",
  "items": []
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    trip_id: "trp_01JC0000000000000000000001",
    store_id: "sto_01JC000000000000000000000B",
    items: [],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "trip_id": "trp_01JC0000000000000000000001",
  "store_id": "sto_01JC000000000000000000000B",
  "items": []
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0Z1P4N2A4WQZ1QT4R

{
  "result": {
    "trip_id": "trp_01JC0000000000000000000001",
    "added": 0,
    "updated": 0,
    "removed_from_list": 0,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 0,
      "on_arrival": 0
    }
  },
  "version": 1
}

A trip with no store takes the one named

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "trip_id": "trp_01JC0000000000000000000001",
  "store_name": "trader joe'\''s",
  "items": []
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    trip_id: "trp_01JC0000000000000000000001",
    store_name: "trader joe's",
    items: [],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "trip_id": "trp_01JC0000000000000000000001",
  "store_name": "trader joe's",
  "items": []
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R034XQE7NZ30VMDCXD

{
  "result": {
    "trip_id": "trp_01JC0000000000000000000001",
    "added": 0,
    "updated": 0,
    "removed_from_list": 0,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 0,
      "on_arrival": 0
    }
  },
  "version": 1
}

A receipt photo is attached

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "trip_id": "trp_01JC0000000000000000000001",
  "receipt_file_id": "fil_01JC000000000000000000000A",
  "items": []
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    trip_id: "trp_01JC0000000000000000000001",
    receipt_file_id: "fil_01JC000000000000000000000A",
    items: [],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "trip_id": "trp_01JC0000000000000000000001",
  "receipt_file_id": "fil_01JC000000000000000000000A",
  "items": []
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0B5WPH0JMCB0EJS7K

{
  "result": {
    "trip_id": "trp_01JC0000000000000000000001",
    "added": 0,
    "updated": 0,
    "removed_from_list": 0,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 0,
      "on_arrival": 0
    }
  },
  "version": 1
}

Items checked on the list are not taken off it

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "shopped_on": "2025-11-06",
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000A"
    }
  ]
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    shopped_on: "2025-11-06",
    items: [
      {
        product_id: "prd_01JC000000000000000000000A",
      },
    ],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "shopped_on": "2025-11-06",
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000A"
    }
  ]
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0ABZDMXVCV4SFJ220

{
  "result": {
    "trip_id": "trp_01JC4N0000MBGE5RJ6Q7V6V6T8",
    "added": 1,
    "updated": 0,
    "removed_from_list": 0,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 1,
      "on_arrival": 0
    }
  },
  "version": 1
}

An order makes a trip with its order number at a new online store, links the email, and waits for it to arrive to put things away

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "store_name": "Amazon",
  "shopped_on": "2025-11-05",
  "order_number": "112-4839201-5551234",
  "email_message_id": "emm_01JC0000000000000000000001",
  "currency": "USD",
  "total_minor": 2398,
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000A",
      "quantity": 12,
      "price_minor": 2398,
      "brand": "Bounty"
    }
  ]
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    store_name: "Amazon",
    shopped_on: "2025-11-05",
    order_number: "112-4839201-5551234",
    email_message_id: "emm_01JC0000000000000000000001",
    currency: "USD",
    total_minor: 2398,
    items: [
      {
        product_id: "prd_01JC000000000000000000000A",
        quantity: 12,
        price_minor: 2398,
        brand: "Bounty",
      },
    ],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "store_name": "Amazon",
  "shopped_on": "2025-11-05",
  "order_number": "112-4839201-5551234",
  "email_message_id": "emm_01JC0000000000000000000001",
  "currency": "USD",
  "total_minor": 2398,
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000A",
      "quantity": 12,
      "price_minor": 2398,
      "brand": "Bounty"
    }
  ]
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0B3066BYBANHB0GDK

{
  "result": {
    "trip_id": "trp_01JC4N0000VGW1STJ6TGXRB7FY",
    "added": 1,
    "updated": 0,
    "removed_from_list": 1,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 0,
      "on_arrival": 1
    }
  },
  "version": 1
}

A later email for the same order records onto its trip, and its delivery puts everything away on the day it arrived

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "store_name": "amazon",
  "order_number": "112-4839201-5551234",
  "arrived_on": "2025-11-07",
  "email_message_id": "emm_01JC0000000000000000000002",
  "items": []
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    store_name: "amazon",
    order_number: "112-4839201-5551234",
    arrived_on: "2025-11-07",
    email_message_id: "emm_01JC0000000000000000000002",
    items: [],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "store_name": "amazon",
  "order_number": "112-4839201-5551234",
  "arrived_on": "2025-11-07",
  "email_message_id": "emm_01JC0000000000000000000002",
  "items": []
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R05GYQ6EWF2PCQSY1V

{
  "result": {
    "trip_id": "trp_01JC000000000000000000000A",
    "added": 0,
    "updated": 0,
    "removed_from_list": 0,
    "put_away": {
      "to_put_away": 1,
      "untracked": 0,
      "no_usual_place": 0,
      "on_arrival": 0
    }
  },
  "version": 1
}

An email already linked to the trip is linked once

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "store_id": "sto_01JC000000000000000000000A",
  "order_number": "112-4839201-5551234",
  "email_message_id": "emm_01JC0000000000000000000001",
  "items": []
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    store_id: "sto_01JC000000000000000000000A",
    order_number: "112-4839201-5551234",
    email_message_id: "emm_01JC0000000000000000000001",
    items: [],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "store_id": "sto_01JC000000000000000000000A",
  "order_number": "112-4839201-5551234",
  "email_message_id": "emm_01JC0000000000000000000001",
  "items": []
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0Y6ZSDFR5G97KQ8K0

{
  "result": {
    "trip_id": "trp_01JC000000000000000000000A",
    "added": 0,
    "updated": 0,
    "removed_from_list": 0,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 0,
      "on_arrival": 0
    }
  },
  "version": 1
}

A line added to an order that has arrived is put away straight away

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "store_id": "sto_01JC000000000000000000000A",
  "order_number": "112-4839201-5551234",
  "currency": "USD",
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000B",
      "price_minor": 399
    }
  ]
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    store_id: "sto_01JC000000000000000000000A",
    order_number: "112-4839201-5551234",
    currency: "USD",
    items: [
      {
        product_id: "prd_01JC000000000000000000000B",
        price_minor: 399,
      },
    ],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "store_id": "sto_01JC000000000000000000000A",
  "order_number": "112-4839201-5551234",
  "currency": "USD",
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000B",
      "price_minor": 399
    }
  ]
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0NT2CJJCR4RXRYFJY

{
  "result": {
    "trip_id": "trp_01JC000000000000000000000A",
    "added": 1,
    "updated": 0,
    "removed_from_list": 0,
    "put_away": {
      "to_put_away": 1,
      "untracked": 0,
      "no_usual_place": 0,
      "on_arrival": 0
    }
  },
  "version": 1
}

An unknown trip is refused

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "trip_id": "trp_01JC0000000000000000000001",
  "items": []
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    trip_id: "trp_01JC0000000000000000000001",
    items: [],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "trip_id": "trp_01JC0000000000000000000001",
  "items": []
}
Response, 404
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0GJEDRG39J4D9HZYV

{
  "error": {
    "code": "trip_not_found",
    "message": "That trip isn't there any more. No changes were applied.",
    "details": {
      "trip_id": "trp_01JC0000000000000000000001"
    },
    "recovery": "Read the current state, then send a command that fits it.",
    "request_id": "req_01K9D9S2R0GJEDRG39J4D9HZYV"
  }
}

An unknown store is refused

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "store_id": "sto_01JC000000000000000000000Z",
  "shopped_on": "2025-11-06",
  "items": []
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    store_id: "sto_01JC000000000000000000000Z",
    shopped_on: "2025-11-06",
    items: [],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "store_id": "sto_01JC000000000000000000000Z",
  "shopped_on": "2025-11-06",
  "items": []
}
Response, 404
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-Id: req_01K9D9S2R01S1TFASM9JQJEBMQ

{
  "error": {
    "code": "store_not_found",
    "message": "That store isn't there any more. No changes were applied.",
    "details": {
      "store_id": "sto_01JC000000000000000000000Z"
    },
    "recovery": "Read the current state, then send a command that fits it.",
    "request_id": "req_01K9D9S2R01S1TFASM9JQJEBMQ"
  }
}

An unknown receipt file is refused

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "trip_id": "trp_01JC0000000000000000000001",
  "receipt_file_id": "fil_01JC000000000000000000000Z",
  "items": []
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    trip_id: "trp_01JC0000000000000000000001",
    receipt_file_id: "fil_01JC000000000000000000000Z",
    items: [],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "trip_id": "trp_01JC0000000000000000000001",
  "receipt_file_id": "fil_01JC000000000000000000000Z",
  "items": []
}
Response, 404
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0CBGP3G8YQVTQEQ5R

{
  "error": {
    "code": "file_not_found",
    "message": "That file isn't there any more. No changes were applied.",
    "details": {
      "file_id": "fil_01JC000000000000000000000Z"
    },
    "recovery": "Read the current state, then send a command that fits it.",
    "request_id": "req_01K9D9S2R0CBGP3G8YQVTQEQ5R"
  }
}

An unknown product is refused

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "shopped_on": "2025-11-06",
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000Z"
    }
  ]
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    shopped_on: "2025-11-06",
    items: [
      {
        product_id: "prd_01JC000000000000000000000Z",
      },
    ],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "shopped_on": "2025-11-06",
  "items": [
    {
      "product_id": "prd_01JC000000000000000000000Z"
    }
  ]
}
Response, 404
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-Id: req_01K9D9S2R00JFKRREVNQ18ZNSF

{
  "error": {
    "code": "product_not_found",
    "message": "That product isn't there any more. No changes were applied.",
    "details": {
      "product_id": "prd_01JC000000000000000000000Z"
    },
    "recovery": "Read the current state, then send a command that fits it.",
    "request_id": "req_01K9D9S2R00JFKRREVNQ18ZNSF"
  }
}

An order number with no trip and no shopped_on is refused

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "store_id": "sto_01JC000000000000000000000A",
  "order_number": "112-4839201-5551234",
  "arrived_on": "2025-11-07",
  "items": []
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    store_id: "sto_01JC000000000000000000000A",
    order_number: "112-4839201-5551234",
    arrived_on: "2025-11-07",
    items: [],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "store_id": "sto_01JC000000000000000000000A",
  "order_number": "112-4839201-5551234",
  "arrived_on": "2025-11-07",
  "items": []
}
Response, 404
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0E4Q5512FJ36F3QGG

{
  "error": {
    "code": "order_not_found",
    "message": "There's no trip for that order yet. Give shopped_on, the day it was ordered, to record it. No changes were applied.",
    "details": {
      "order_number": "112-4839201-5551234"
    },
    "recovery": "Read the current state, then send a command that fits it.",
    "request_id": "req_01K9D9S2R0E4Q5512FJ36F3QGG"
  }
}

An unknown email is refused

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "store_id": "sto_01JC000000000000000000000A",
  "shopped_on": "2025-11-05",
  "order_number": "112-4839201-5551234",
  "email_message_id": "emm_01JC000000000000000000000Z",
  "items": []
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    store_id: "sto_01JC000000000000000000000A",
    shopped_on: "2025-11-05",
    order_number: "112-4839201-5551234",
    email_message_id: "emm_01JC000000000000000000000Z",
    items: [],
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "store_id": "sto_01JC000000000000000000000A",
  "shopped_on": "2025-11-05",
  "order_number": "112-4839201-5551234",
  "email_message_id": "emm_01JC000000000000000000000Z",
  "items": []
}
Response, 404
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-Id: req_01K9D9S2R098JK1BXSPJ4EV842

{
  "error": {
    "code": "message_not_found",
    "message": "That email isn't here any more. No changes were applied.",
    "details": {
      "email_message_id": "emm_01JC000000000000000000000Z"
    },
    "recovery": "Read the current state, then send a command that fits it.",
    "request_id": "req_01K9D9S2R098JK1BXSPJ4EV842"
  }
}

Add a receipt to a trip

Lines for products the trip has fill in their price and brand. Other lines, such as the bananas, are added to the trip.

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01M6V9CT8P5MTRPKTF0CB11DWM' \
  -d '{
  "trip_id": "trp_01M6V9CT8JBJVJK865ZH4D2ZZD",
  "items": [
    {
      "product_name": "Milk",
      "price_minor": 778,
      "brand": "Hillside"
    },
    {
      "product_name": "Crushed tomatoes",
      "price_minor": 258
    },
    {
      "product_name": "Bananas",
      "quantity": 6,
      "price_minor": 189
    }
  ],
  "total_minor": 1225,
  "currency": "USD"
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01M6V9CT8P5MTRPKTF0CB11DWM",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    trip_id: "trp_01M6V9CT8JBJVJK865ZH4D2ZZD",
    items: [
      {
        product_name: "Milk",
        price_minor: 778,
        brand: "Hillside",
      },
      {
        product_name: "Crushed tomatoes",
        price_minor: 258,
      },
      {
        product_name: "Bananas",
        quantity: 6,
        price_minor: 189,
      },
    ],
    total_minor: 1225,
    currency: "USD",
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01M6V9CT8P5MTRPKTF0CB11DWM

{
  "trip_id": "trp_01M6V9CT8JBJVJK865ZH4D2ZZD",
  "items": [
    {
      "product_name": "Milk",
      "price_minor": 778,
      "brand": "Hillside"
    },
    {
      "product_name": "Crushed tomatoes",
      "price_minor": 258
    },
    {
      "product_name": "Bananas",
      "quantity": 6,
      "price_minor": 189
    }
  ],
  "total_minor": 1225,
  "currency": "USD"
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01M6V9CT8PH67G0VVD7WM3VTBZ

{
  "result": {
    "trip_id": "trp_01M6V9CT8JBJVJK865ZH4D2ZZD",
    "added": 1,
    "updated": 2,
    "removed_from_list": 0,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 1,
      "on_arrival": 0
    }
  },
  "version": 286
}

Record an online order

Naming the store by store_name uses the existing store, or makes one. order_number ties later emails about the order to the same trip.

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/commands/shopping.trip.record' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01M6V9CT8S2QVPNFGGPDJBPTG8' \
  -d '{
  "store_name": "Pantry Direct",
  "shopped_on": "2026-11-04",
  "order_number": "PD-48213",
  "items": [
    {
      "product_name": "Paper towels",
      "quantity": 12,
      "unit": "roll",
      "price_minor": 2899,
      "brand": "Softcore"
    },
    {
      "product_name": "Dish soap",
      "price_minor": 449
    }
  ],
  "total_minor": 3348,
  "currency": "USD"
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/commands/shopping.trip.record", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01M6V9CT8S2QVPNFGGPDJBPTG8",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    store_name: "Pantry Direct",
    shopped_on: "2026-11-04",
    order_number: "PD-48213",
    items: [
      {
        product_name: "Paper towels",
        quantity: 12,
        unit: "roll",
        price_minor: 2899,
        brand: "Softcore",
      },
      {
        product_name: "Dish soap",
        price_minor: 449,
      },
    ],
    total_minor: 3348,
    currency: "USD",
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/commands/shopping.trip.record HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01M6V9CT8S2QVPNFGGPDJBPTG8

{
  "store_name": "Pantry Direct",
  "shopped_on": "2026-11-04",
  "order_number": "PD-48213",
  "items": [
    {
      "product_name": "Paper towels",
      "quantity": 12,
      "unit": "roll",
      "price_minor": 2899,
      "brand": "Softcore"
    },
    {
      "product_name": "Dish soap",
      "price_minor": 449
    }
  ],
  "total_minor": 3348,
  "currency": "USD"
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01M6V9CT8S6ENS1JWK7QNFE9XE

{
  "result": {
    "trip_id": "trp_01M6V9CT8S4T83B9C9T7FBKE1N",
    "added": 2,
    "updated": 0,
    "removed_from_list": 0,
    "put_away": {
      "to_put_away": 0,
      "untracked": 0,
      "no_usual_place": 1,
      "on_arrival": 1
    }
  },
  "version": 287
}

For Rigel#

Rigel's tool for this command is shopping_trip_record. Rigel runs this on its own, then says what it did. The agent guide has the rest.

Rigel is in early access, and sign-up is by invitation. Open Rigel