Skip to main content
Conventions that hold across the whole API. Individual endpoints document what is specific to them; everything here is assumed.

Requests

Unknown fields are rejected, not ignored. Sending a property the endpoint does not define returns 400 with "property <name> should not exist". This turns a typo into an immediate error instead of a silently dropped value — but it also means you cannot round-trip a response object straight back into a PATCH. Send only the fields you intend to change.

List responses

List endpoints are not paginated. They return a complete JSON array of every matching record your team can see. There are no page, limit, offset, or cursor parameters, and no pagination envelope.
This is fine at current catalog and order volumes and keeps clients simple. It does mean a list response grows with your data, so:
  • Filter server-side where you can. GET /reseller/catalog accepts category, minPrice, maxPrice, zipCode, and search; narrowing there is far cheaper than fetching everything and filtering locally.
  • Do not assume a bounded response size in your client — no fixed buffers, no hard-coded array-length expectations.
  • Prefer the detail endpoint when you already know the id. GET /reseller/orders/{id} beats scanning GET /reseller/orders.
Pagination will be added additively — an opt-in query parameter with the unpaginated array as the default response — so it does not break existing clients. It will be announced in the changelog before it ships. If unbounded lists are already a problem for you, tell us at support@offergrid.io.

Filtering

Filters are query parameters, and they combine with AND. An unknown query parameter is rejected with 400, the same as an unknown body field.

Identifiers

Every resource id is a UUID v4 string.
Treat them as opaque: do not parse them, infer type or ordering from them, or assume any length other than 36 characters. Ids are stable for the life of the resource. The one exception is a shareable link’s slug, which appears in public URLs and is a short human-shareable token rather than a UUID.

Timestamps

All timestamps are ISO 8601 in UTC, with milliseconds and a trailing Z.
Send them in the same format. Date-only fields — a move-in date, an installation date — are calendar dates with no time component and no timezone; do not convert them through a local timezone or they can shift by a day.

Money

Monetary values are returned as strings, not numbers.
Parse money with a decimal library, not parseFloat. These are exact decimal values; binary floating point cannot represent them exactly, and rounding drift on prices and totals shows up in customer-visible numbers. Strings are used precisely so no precision is lost in transport.
All amounts are in USD, monthly, and before taxes and fees unless the offer states otherwise. Prices on an order are snapshotted at order time — an offer whose price changes later does not retroactively change existing orders.

Nulls and optional fields

A field that does not apply is null rather than absent, so response shapes stay stable. Two exceptions are genuinely conditional and absent when they do not apply:
  • electricity — present only on electricity offers
  • internet — present only on internet offers
Every offer carries a top-level serviceType discriminator ("electricity", "internet", …), so branch on that rather than probing for the grouped object.
Responses are additive over time. New fields can appear in any response without notice, so parse permissively and ignore what you do not recognize — a strict parser that rejects unknown response fields will break on a routine release. See Versioning.

Idempotency

There is no Idempotency-Key header today. POST /reseller/orders is not idempotent: two identical requests create two orders. If a write times out without a response, reconcile before retryingGET /reseller/orders will tell you whether the first attempt landed. See Handling errors. Reads, PATCH, and DELETE are naturally idempotent and safe to retry.

Next steps

Errors

Every status code and a retry-safe client

Rate limits

What is limited, and what is not

Versioning

What we promise not to break

Authentication

Keys, headers, roles, and rotation