Docs
Concepts

IDs, objects and formats

How Agoo formats IDs, objects, timestamps, time zones, phone numbers and money, and how to read them safely.

Preview· P9

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)
PrefixObject
orgOrganisation
siteSite (a location)
gateGate (an entrance at a site)
devDevice (a paired kiosk)
personPerson (a host or employee)
visitVisit
visitorVisitor (one person across all their visits)
deliveryDelivery
btypeBooking type
bookingBooking
clockAttendance event (a clock-in or clock-out)
shiftShift
watchWatchlist entry
rollcallRoll call
whepWebhook endpoint
evtEvent
auditAudit event
formForm
keyAPI 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_at instead.
  • The prefix tells you the type at a glance, which helps in logs and when a webhook's data.object could 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, so visit.checked_out_at is null until the visitor leaves. Lists are [] when empty.
  • New fields can appear at any time, and new values can be added to enums such as status or method. 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 its gates and devices.
  • 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:00 or 2026-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 with validation_failed.

Dates and local times

Some values are about a place's calendar rather than an instant:

ValueFormatMeaning
from, to on availability and attendance summaries2026-10-14Whole days in the site's (or booking type's) time zone
start_time, end_time on shifts08:00Wall-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 itIn 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.

On this page