Skip to main content
MeetCost exposes a versioned REST API that every client — the Google Meet add-on, the Zoom app, the Teams tab, and your own tooling — talks to. The full interactive reference is available at /docs on any running deployment, and the OpenAPI document is at /openapi.json. This page covers the conventions and invariants that the schema alone cannot express: why money is a string, what it means when cost.visible is false, and what the health endpoints tell you about the deployment’s readiness.

Base URL

All versioned endpoints are prefixed with /api/v1/. The two health endpoints (/healthz, /readyz) and the webhook receivers (/webhooks/...) live outside this prefix.

Content type

All request and response bodies use application/json. Requests with a body must set Content-Type: application/json.

Money is always a string

Monetary amounts cross the wire as strings — "649.38", not 649.38. A JSON number is an IEEE-754 double-precision float, which cannot represent values like 1234.56 exactly. Because these figures end up in finance reports, MeetCost serialises every amount as a decimal string. Parse them with a decimal library, not a float.
This applies to every money field across every endpoint: total, annualised_total, initial_total, final_total, estimated_saving, and all report totals.

Redaction is not an error

Every cost response carries a cost.visible flag. When visible is false, the deployment’s disclosure policy withheld the total from this caller — a normal, expected outcome, not a failure condition. The redactions array tells you which rule applied.
Headcount and duration are always present even when cost is withheld — both are already on the calendar invitation. A client must render the withheld case gracefully rather than treating it as an error, and must never infer a total from other fields in the payload. Redaction happens server-side so that a compromised client has nothing to leak.

Redaction codes

Health and bootstrap endpoints

These endpoints require no authentication.

GET /healthz

Liveness probe. Returns {"status": "ok", "version": "..."}. A load balancer should route to instances where this returns 200.

GET /readyz

Readiness probe. Touches the database, reports the active egress allowlist, and confirms whether the encryption key is configured. Use this for startup and rolling-update gates.

GET /api/v1/client-config

Bootstrap payload for add-in clients. Requires authentication. Returns the org’s currency, nudge thresholds, the caller’s role, and the service version — nothing about pay data.
A sample /readyz response:
/api/v1/client-config deliberately contains nothing about pay data. A compromised add-in client learns the org’s nudge thresholds and nothing else.

Error responses

All errors follow the standard FastAPI/Starlette error shape:

Authentication

See Authentication for how to obtain and pass a bearer token.