IDs, objects and formats
How Agoo formats IDs, objects, timestamps, time zones, phone numbers and money, and how to read them safely.
This is a published preview. Names and fields may still change before general availability; changes will be listed in the changelog.
Every Agoo object follows the same conventions, so once you know them for visits you know them for bookings, people and everything else.
IDs
IDs are TypeIDs: a prefix that says what the object is, an underscore, and 26 lowercase characters.
visit_01m4k5z4j0fxbte6sn6e8tpgza
└─┬─┘ └────────────┬───────────┘
prefix unique part (a UUIDv7 in base32)| Prefix | Object |
|---|---|
org | Organisation |
site | Site (a location) |
gate | Gate (an entrance at a site) |
dev | Device (a paired kiosk) |
person | Person (a host or employee) |
visit | Visit |
visitor | Visitor (one person across all their visits) |
delivery | Delivery |
btype | Booking type |
booking | Booking |
clock | Attendance event (a clock-in or clock-out) |
shift | Shift |
watch | Watchlist entry |
rollcall | Roll call |
whep | Webhook endpoint |
evt | Event |
audit | Audit event |
form | Form |
key | API key (appears as an actor in the audit trail) |
Treat IDs as opaque strings:
- Store them as text. The longest today is 35 characters; allow for 64.
- Compare them exactly. They're case-sensitive and always lowercase.
- Don't parse them. The unique part happens to be time-ordered, but sort by
created_atinstead. - The prefix tells you the type at a glance, which helps in logs and when a webhook's
data.objectcould be one of several things.
Request IDs (req_…) aren't object IDs. Every response carries one in the Agoo-Request-Id header; quote it when you contact support.
Objects
- Every documented field is always present. A field without a value is
null, not missing, sovisit.checked_out_atisnulluntil the visitor leaves. Lists are[]when empty. - New fields can appear at any time, and new values can be added to enums such as
statusormethod. Ignore fields you don't use, and handle values you don't recognise, for example by logging them. See versioning. - Related objects are referenced by ID. A visit has a
host_id, not an embedded host. There's no option to expand related objects in the same response; fetch them from their own endpoint, such as GET/people/{person_id}. Things that rarely change, such as sites and forms, are worth caching. - A few small summaries are embedded where you nearly always need them: a visit includes its
visitor(name, phone, email, company) and a site includes itsgatesanddevices. - Custom fields live in
custom_fields, keyed by the field keys your organisation defines. See custom fields.
Timestamps
Timestamps are RFC 3339 strings in UTC, with a Z:
{ "expected_at": "2026-10-14T09:30:00Z" }- In requests you can send any offset, for example
2026-10-14T09:30:00+00:00or2026-10-14T10:30:00+01:00. Agoo stores the instant and always responds in UTC. - Always include an offset or
Z. A timestamp without one is rejected withvalidation_failed.
Dates and local times
Some values are about a place's calendar rather than an instant:
| Value | Format | Meaning |
|---|---|---|
from, to on availability and attendance summaries | 2026-10-14 | Whole days in the site's (or booking type's) time zone |
start_time, end_time on shifts | 08:00 | Wall-clock time at the site |
Time zones
Each site has an IANA time zone, such as Africa/Accra, in site.time_zone. Agoo uses it to decide when a site's day starts and ends, when a shift starts and what "today" means in a summary.
Ghana is on UTC all year, with no daylight saving, so in Accra local time and UTC are the same. Don't rely on that in your code: organisations in Lagos (Africa/Lagos, UTC+1) or Nairobi (Africa/Nairobi, UTC+3) work the same way with different offsets. Convert with the site's time zone, not a fixed offset.
Phone numbers
Phone numbers are in E.164 format: a +, the country code and the number, with no spaces or punctuation.
| As people write it | In the API |
|---|---|
024 123 4567 | +233241234567 |
+233 20 555 0142 | +233205550142 |
For a Ghanaian number written locally, drop the leading 0 and add +233. The API doesn't guess: a number that isn't E.164 returns validation_failed with the field's location, for example body.visitor.phone.
In a query string, encode the + as %2B, because a bare + means a space:
curl "https://api.agoo.ardent.africa/v1/visitors?phone=%2B233241234567" \
-H "Authorization: Bearer $AGOO_API_KEY"Money
Amounts are an integer number of the currency's minor unit with an ISO 4217 currency code, never a decimal or a float:
{ "amount": 29900, "currency": "GHS" }That's GH₵299.00: 29,900 pesewas. To display it, divide by 100 for GHS and format with the currency, for example with Intl.NumberFormat("en-GH", { style: "currency", currency: "GHS" }). In v1, money appears in the organisation's wallet_balance.