Skip to content
Rigel docs
Open Rigel

Households and people

A household is one family's workspace, and the people in it are not the same thing as the accounts that sign in.

The household#

Everything in Rigel belongs to a household: its people, meals, recipes, shopping, inventory, calendar, email, tasks, documents, chat, home maintenance and files. A household has an ID that starts with hh_, and that ID is in the path of every household route. Your own list is in GET /api/v1/me, and one household's settings are in GET /api/v1/households/{household_id}.

Get a household

Request, curl
curl 'https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95' \
  -H "Cookie: rigel_session=$RIGEL_SESSION"
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95", {
  headers: {
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
});
const data = await response.json();
Request, HTTP
GET /api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95 HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01M6V9CS6YB5Q24RZNZ5KWYQCZ

{
  "id": "hh_01M6V9CS6GGVFCY7NAS8ZTQV95",
  "name": "The Okafors",
  "time_zone": "America/Chicago",
  "role": "admin"
}
FieldMeaning
idThe household's ID, hh_ and 26 characters.
nameWhat the household calls itself, such as "The Okafors".
time_zoneAn IANA time zone, such as America/Chicago.
roleYour role in this household. admin is the only role for now.

POST /api/v1/households creates a household with you as its admin and adds you to its family. The name is 1 to 80 characters. The time zone must be an IANA name, or UTC. A response of 201 carries the new household.

curl
curl -sS -X POST https://rigel.house/api/v1/households \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H "Content-Type: application/json" \
  -d '{ "name": "The Okafors", "time_zone": "America/Chicago" }'

Accounts and people#

Someone in a family can appear in Rigel in two ways, and each has its own ID.

AccountPerson
What it isSomeone who signs in with Google.Someone in a household's immediate family.
IDusr_, one for the whole of Rigel.per_, one for each household.
Where you see itGET /api/v1/mefamily.person.list, and any command that says who something is for.
Can have severalMemberships, one in each household.Notes, meals, calendar events.

A member is an account that belongs to a household, and each membership is linked to that household's person for them. Children are people without accounts. A child can be linked to an account later. Only immediate family count as people. Grandparents, a nanny or a friend aren't people in Rigel, and what the household knows about them goes in a note.

In the Okafors' household, Ada has an account. Ada, Ben, Nia and Theo are all people. Commands that say who something is for, such as a meal or a note, take a per_ ID, never a usr_ ID. family.person.list marks the ones who sign in with signs_in.

Roles#

Every member who signs in is an admin for now, and an admin can run every command and query. Your role shows in GET /api/v1/me and on the household.

Roles are stored and checked already, so a fuller system can be added without changing the routes. A command can name the roles allowed to run it. If yours isn't one of them, the request is refused with 403 forbidden_role and nothing changes. Only household.sample.clear and the sample data route name a role so far, admin, which every member has. The roles system will be designed before anyone who isn't an admin can join a household.

The household's time zone#

A household has one time zone, set when it is created. It can't be changed through the API yet. It decides what a day is for that family, so it affects what you get back and what you should send.

  • Calendar dates are YYYY-MM-DD with no time zone, and they mean a day in the household's time zone. A meal planned for 2026-11-10 is on that day for the family, wherever your script runs.
  • Timestamps in responses are UTC, in ISO 8601 form, such as 2026-11-06T18:00:00Z, with a few exceptions that IDs and formats lists.
  • Repeating times, such as "Wednesdays at 9am", are kept as local times in the household's time zone, so they stay at 9am when the clocks change.
  • Answers computed from today's date use the household's today. The age that family.person.list gives in words, and "this week" in the calendar and meal plan, are worked out there.
  • The time recorded for a change is a UTC instant. For a command sent over HTTP it is when the server received the request. From an app it is when the person acted, even if the command syncs later.

At 11:30 pm on November 5 in Chicago it is already November 6 in UTC. If your script needs "today" for a household, work it out in the household's time zone:

TypeScript
const today = new Intl.DateTimeFormat("en-CA", {
  timeZone: "America/Chicago", // the household's time_zone
  year: "numeric",
  month: "2-digit",
  day: "2-digit",
}).format(new Date());
// "2026-11-05"

Each household is kept apart#

Each household's records live in a store of its own. A query in one household cannot see another's rows, because they aren't in the same database. Nothing is shared between households, including people, recipes and product names. What spans them is your account: your name, your email and your list of memberships.

The API checks your membership before it reaches a household. Using the ID of a household you don't belong to returns 404, the same answer as for a household that doesn't exist. An ID from one household that you pass to another household is not found there. Holding an ID gives you no access. See 401 and 404.

A script that works with two households sends each request to the right household's path. It keeps their IDs, their people and their records separate.

Outside accounts#

A member can connect a Google account so that Calendar, Gmail or Drive works for the whole household. The connection belongs to the household, any member can see it, and any member can disconnect it with household.integration.disconnect. Connecting happens in a browser. See outside accounts.

Notifications#

A notification tells one person that something is worth their attention: a mention, a task assigned to them, the dentist in 30 minutes. It is recorded for people who have an account, one for each person, so the list and the unread count reach every device like any other change.

Notifies each person with an account once, in order, leaving out me and people without accounts

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/notifications.notification.send' \
  -H "Cookie: rigel_session=$RIGEL_SESSION" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA' \
  -d '{
  "person_ids": [
    "per_01JC000000000000000000000B",
    "per_01JC000000000000000000000A",
    "per_01JC000000000000000000000C",
    "per_01JC000000000000000000000B"
  ],
  "title": "Ada in Summer trip"
}'
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/notifications.notification.send", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": "cmd_01JC4N0000AAAAAAAAAAAAAAAA",
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
  body: JSON.stringify({
    person_ids: [
      "per_01JC000000000000000000000B",
      "per_01JC000000000000000000000A",
      "per_01JC000000000000000000000C",
      "per_01JC000000000000000000000B",
    ],
    title: "Ada in Summer trip",
  }),
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01K9BKKP003AHRZ6DMV29GQY5C/commands/notifications.notification.send HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Content-Type: application/json
Idempotency-Key: cmd_01JC4N0000AAAAAAAAAAAAAAAA

{
  "person_ids": [
    "per_01JC000000000000000000000B",
    "per_01JC000000000000000000000A",
    "per_01JC000000000000000000000C",
    "per_01JC000000000000000000000B"
  ],
  "title": "Ada in Summer trip"
}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0P7W62VBMKF5Q1W6X

{
  "result": {
    "notified": [
      {
        "person_id": "per_01JC000000000000000000000B",
        "notification_id": "ntf_01JC4N0000MBGE5RJ6Q7V6V6T8"
      }
    ],
    "skipped": [
      {
        "person_id": "per_01JC000000000000000000000A",
        "reason": "self"
      },
      {
        "person_id": "per_01JC000000000000000000000C",
        "reason": "no_account"
      }
    ]
  },
  "version": 1
}

Write a notification's title as who and where, such as "Ada in Summer trip". It is the only text that reaches a lock screen. The body, up to 500 characters, shows only inside the app. A read notification is deleted 30 days after it was read, and any notification 90 days after it was delivered.

Browsers also get a web push when a notification is due. A push is sent only if it is still unread, hasn't expired (a day after delivery unless expires_at says otherwise) and its app isn't muted. Three routes manage a browser's subscription. GET /api/v1/push/config returns the public key to subscribe with, or null when push isn't set up on the server. PUT /api/v1/me/push-subscription saves the browser's subscription for your current session, and DELETE /api/v1/me/push-subscription removes it. A subscription ends when its session does, so signing out stops pushes to that browser. A script has no use for them. Android delivery isn't built yet.

Sample data#

To see Rigel with records in it, POST /api/v1/households/{household_id}/sample-data fills the household with a made-up family's records. It adds people and notes, a pantry, recipes, a meal plan, a calendar, tasks, a family chat, mail, documents and a home with its equipment, through the same commands people use, so the history shows them. Dates are worked out from the day you add it, in the household's time zone, so the plan is this week's.

Fill a household with a made-up family

Every record it adds is marked as sample data, so one command can take it all out again.

Request, curl
curl -X POST 'https://rigel.house/api/v1/households/hh_01M6V9CTF2YQWZD4QYP8NGFJXF/sample-data' \
  -H "Cookie: rigel_session=$RIGEL_SESSION"
Request, TypeScript
const response = await fetch("https://rigel.house/api/v1/households/hh_01M6V9CTF2YQWZD4QYP8NGFJXF/sample-data", {
  method: "POST",
  headers: {
    Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
  },
});
const data = await response.json();
Request, HTTP
POST /api/v1/households/hh_01M6V9CTF2YQWZD4QYP8NGFJXF/sample-data HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Response, 201
HTTP/1.1 201 Created
Content-Type: application/json
X-Request-Id: req_01M6V9CTFBDMHWK82Q29PCMKDE

{
  "records": 1258,
  "apps": [
    {
      "app": "family",
      "records": 19
    },
    {
      "app": "calendar",
      "records": 73
    },
    {
      "app": "tasks",
      "records": 117
    },
    {
      "app": "inventory",
      "records": 136
    },
    {
      "app": "maintenance",
      "records": 122
    },
    {
      "app": "recipes",
      "records": 255
    },
    {
      "app": "meals",
      "records": 217
    },
    {
      "app": "shopping",
      "records": 83
    },
    {
      "app": "chat",
      "records": 147
    },
    {
      "app": "email",
      "records": 66
    },
    {
      "app": "documents",
      "records": 20
    },
    {
      "app": "dashboard",
      "records": 3
    }
  ],
  "failed": null
}
  • Nothing already in the household is changed, and nothing reaches outside it. No email is sent, no account is connected, and nothing is uploaded. Notifications it sends to real members arrive already read.
  • Every record it makes is marked as sample data. The answer is 201 with the number of records added, apps (each app and how many it added, in order) and failed. failed is null when everything went in. Otherwise it names the app that stopped partway and the error code, and what was added before it is still marked and can be cleared.
  • Only an admin can add it, which is every member today. While the household still has sample data, adding it again is 409 sample_exists, with the count in details.records. Clear it first.
  • Product icons and app pictures the household is missing are queued afterward, as they are for any household, if its model is set up.

household.sample.clear takes it all out again. It deletes every marked record that still exists, including anyone's edits to those records, and answers with removed and kept. A sample record that something else now uses is kept as ordinary data, along with what it needs: a real item of a sample product, or a note someone wrote about a sample person. Nothing anyone added themselves is deleted or changed, and clearing when there is no sample data does nothing. It is open to Rigel, so asking Rigel to clear it works too.

Deletes every marked row and forgets the marks, leaving everything else

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

{}
Response, 200
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_01K9D9S2R0QNXS397G5ZVJG59Z

{
  "result": {
    "removed": 2,
    "kept": 0
  },
  "version": 1
}

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