Skip to main content

The whole thing in five sentences

  1. You publish offers on Offergrid, each covering a list of ZIP codes.
  2. ZIP codes are too coarse when availability is really decided building by building, so you expose one endpoint that answers “can you serve this exact address, and at what price?”
  3. We call it the moment a shopper types a full address, and use your answer to confirm or hide your offers and to show your real price.
  4. We call it once more at checkout, so nobody orders something you can’t actually deliver.
  5. The order lands in your Offergrid dashboard and, if you want, as a signed webhook to your own system.
In plain words: someone knocks on your door holding an address. You say yes or no. If yes, you say what you can sell them and for how much. That’s the entire integration.
The whole integration. Everything in the Your side lane is what you build — one endpoint, plus whatever already receives your orders. Note that the same endpoint is called twice: once while the shopper is browsing, once at checkout with the cache deliberately bypassed.

Why ZIP codes aren’t enough

Most offers are listed against a ZIP-code footprint: if the customer’s ZIP is in your service area, the offer shows. That is coarse for services where availability is decided at the individual address — wired internet, fixed wireless, anything with a physical network path to the building. A ZIP code can contain 20,000 homes when you only reach 6,000 of them. Listing the offer across the whole ZIP shows it to people you can’t serve; listing it nowhere hides it from people you can. Both cost you orders. A serviceability integration is how you get out of that trade.
In plain words: the ZIP code is the neighborhood. The endpoint is the actual house.
This is optional. Offers without a serviceability source work exactly as they always have — ZIP-level availability and list pricing.

What you set up first

Three things come before serviceability matters at all. All are ordinary product setup in the dashboard, and none need engineering.

Three ways to onboard

1. Build to this spec

You expose an endpoint shaped like the contract below. Offergrid-side setup is one integration record and one secret. Fastest path, no mapping work.

2. We map your existing API

Your API stays exactly as it is. We describe it in the integration’s config — which fields carry the address, where the products live in your response. No code on either side.

3. Custom adapter

For APIs that can’t be expressed as a field map (multi-step handshakes, session tokens, non-JSON payloads). Engineering-scoped — talk to us early.
Tier 2 covers most existing serviceability APIs, so you do not need to build anything new to integrate. Tier 1 exists because it is the cheapest to stand up and the easiest to support: if you’re building the endpoint from scratch anyway, build it to this shape and the entire integration is configuration. The rest of this page describes the tier-1 contract. Mapping an existing API at the end covers tier 2.

The contract

Request

Offergrid sends a JSON POST from its servers — never from the shopper’s browser, so your endpoint is never exposed to end users. If it requires source-IP allow-listing, contact support before you build, so we can confirm what we can commit to.
We only call once the address is complete enough to be worth asking about — by default line1, city, state, and zipCode must all be present. On ZIP-only surfaces (catalog browse, map search) the integration stays dormant and your offers fall back to their ZIP footprint. That threshold is configurable per integration.
Authentication is a single static header: you name the header, we send a secret value in it. The value is sent verbatim, so if your scheme needs a prefix (Bearer …, Token …), include it in the secret itself. The secret is held as a platform environment variable on our side and is never stored in our database or shown in the dashboard. POST is required. The connector sends the address in the request body, so a GET-only endpoint has no way to receive it.

Response

Return 200 with this JSON:
boolean
required
Whether you can serve this address. This is the answer that gates or confirms availability.
array
What is purchasable at this address. Empty (or omitted) when serviceable is false.
Extra fields are fine and ignored. Fields you can’t supply should be omitted rather than sent as empty strings.

Rules

Return 200 with "serviceable": false and an empty products array. A non-2xx status means we failed to get an answer, which we handle very differently (see below) from the answer is no.
On a non-2xx status, a timeout, or unparsable JSON, Offergrid falls back to the most recent cached answer for that address; with no cached answer, the integration goes quiet and your offers behave as if they had no serviceability source (ZIP-level availability, list pricing). Nothing breaks and no order is lost — but nothing is confirmed either. Prefer 200 with serviceable: false over an error whenever you actually know the answer.
The call runs while a shopper waits for offer cards to render. Sub-second is ideal. If your upstream is slow, cache on your side — the request is the same normalized address every time.
We may call the same address more than once: on browse, when the cached answer expires, and again at checkout. The call must not create a lead, consume a quota, or otherwise mutate state on your side.
serviceable: false with a non-empty products array is contradictory, and different parts of the platform may read either field. When you can’t serve the address, say so and return no products.

What Offergrid does with the answer

Caching. Answers are cached per (integration, address) with a TTL — 24 hours by default, configurable per integration. Within a single page render, one address costs exactly one call to you no matter how many of your offers are on screen. Coverage: confirm or gate. When you attach the integration to a service area you choose one of two behaviors:
In plain words: confirm means “show it, then tell them the truth”. Gate means “don’t even show it unless I say yes”. Gate is honest but unforgiving; confirm sells more but shows offers you may have to decline.
Pricing, per offer. This is the part people get wrong, so here it is concretely. You return two products in one response; on Offergrid you have two offers. Each offer stores the key of the product it represents — the field labelled “Product identifier in your system” in the offer editor. One call to you, two cards, two correct prices. An offer with no key — or a key that isn’t in the response — falls back to the first product in the array, which is usually not what you want, so set them all. Checkout re-verification. At order submit, Offergrid calls you again, bypassing the cache, against the order’s service address:
  • On-net → the fresh price, plan, technology, and install fee are recorded on the order alongside the price the customer agreed to, so fulfillment (and any dispute) can compare the two.
  • Off-net on a gating service area → the order is rejected at review with a clear message. The customer never places an order you’d have to cancel.
  • No answer (your endpoint is unreachable) → the order proceeds and is flagged as unverified. Serviceability is never a payment gate.
Offers with no serviceability source record nothing here, which stays distinguishable from “we asked and got nothing back”.

How the order reaches you

Orders arrive from resellers placing them on a customer’s behalf, from a public link a reseller shared, or from Offergrid’s consumer storefront. However it started, it lands the same way. You move each item along as you fulfill it, from the dashboard or through the API. Resellers and customers are notified automatically at the points that matter to them.
Order item lifecycle
In plain words: you told us who you can serve. We found them, quoted your price, and checked with you again before taking the order. Now it’s a normal order in your queue.

Connecting it

1

Publish the endpoint

Deploy it and confirm it answers a known-serviceable and a known-unserviceable address correctly.
2

Send us the secret

Share the auth header value with support through a secure channel. We set it as a platform environment variable and reference it by name — it never enters the database.
3

Create the integration

In your dashboard, go to IntegrationsNew integration, pick a Source key (a short, permanent identifier for this connection, e.g. acme-serviceability — it can’t be changed later, because your service areas point at it), and paste the config below. It’s validated on save, so typos surface as field errors rather than silent no-ops.
4

Test it

Use Test lookup on the integration to run a real address through the live endpoint, bypassing the cache. It reports the on-net answer, the projected price fields, whether the result came from cache or live, and the upstream error verbatim if the call failed.
5

Attach it to coverage

Either add a Serviceability area to a service area (picking the integration and the confirm-or-gate choice), or — for internet offers — do it inline from the offer’s Service areas step, which writes the ZIP footprint and the serviceability connection together.
6

Set each offer's product identifier

In the offer editor, set Product identifier in your system to the product’s key. Do this for every offer that should price from this integration.

Config for a spec-conformant endpoint

Because the request fields and response shape already match, the config is near-identity — it declares the endpoint, the auth header, and the (1:1) mapping:
What each block does:
  • request.body — how address fields become your request body. Here the names are identical on both sides.
  • response.onNetWhenAnyNonEmpty — the paths that decide the on-net answer: on-net when any of them holds a truthy value or a non-empty array. For this contract the serviceable boolean answers it directly. (["products"] is equivalent for a conformant endpoint, since an unserviceable address returns no products.)
  • display — where to read price/plan fields when an offer has no product identifier. Points at the first product.
  • products — where the product list lives (path), which field identifies a product (keyPath, matched against the offer’s product identifier), and where to read each display field inside the matched entry.
  • ttlMs — how long a cached answer stays fresh. 24 hours here.

Mapping an existing API (tier 2)

If your serviceability API already exists and can’t change shape, the same config describes it — only the values differ:
  • Different field names? "address1": { "field": "line1" } sends our line1 as your address1. Constants your API requires (a partner ID, a promo code) are declared inline: "clientName": { "const": "offergrid" }.
  • No serviceable boolean? Point onNetWhenAnyNonEmpty at the arrays that imply availability, e.g. ["products", "fixedWirelessProducts"] — on-net when any is non-empty.
  • Products nested elsewhere? products.path is a dot-path: "data.availablePlans" works. keyPath can be any stable identity field in an entry — "serviceId", "planCode", even "name".
  • Prices in a nested object? Every display path is a dot-path relative to the matched product entry: "monthlyPricePath": "pricing.monthly.amount".
The mapping language is deliberately small — field copies, constants, dot-paths, and one on-net predicate. It has no conditionals, expressions, or templating, which is what keeps a new integration a support conversation rather than an engineering project. An API that genuinely can’t be expressed this way is tier 3.
In plain words: send us a sample request and a sample response from whatever you already have, and we’ll tell you which tier you’re in.

What’s automated today

So you can plan operations around what exists rather than what’s described above in the abstract.
The fresh call at submit is live for orders placed through Offergrid’s consumer storefront. Reseller-placed and shared-link orders are checked against your endpoint while the reseller browses — including the gate — but don’t yet make a second call at submit. Extending it is in progress.
Auth values are held as platform environment variables, so a new secret needs us to install it before your integration goes live. That’s deliberate — it keeps credentials out of the database — but it makes step 2 a short back-and-forth rather than self-serve.
Orders reach you as signed webhooks and in the dashboard. There is no structured submission into your order-entry system: no passing your quote or session identifier back, no install-window selection, no payment details. Fulfillment starts from the order we hand you.
The one-step “set coverage inside the offer” flow exists for internet offers. Other categories set the same thing up from the Service areas page — same engine, one more click.

Checklist

Endpoint accepts POST with a JSON body and returns 200 JSON
Unserviceable addresses return 200 with serviceable: false and no products
Every product carries a stable key that won’t change
Prices are plain decimals with no currency symbol
Missing line2/country are tolerated (absent, not empty string)
Responses land in under 2 seconds
Repeat calls for the same address are safe and side-effect free
Auth is a single static header whose value we can hold as a secret
Every offer that should price from the endpoint has its product identifier set
Each service area is explicitly set to confirm or gate — chosen, not defaulted
Questions, or an API that doesn’t fit? Email support@offergrid.io with a sample request and response and we’ll tell you which tier you’re in.