The whole thing in five sentences
- You publish offers on Offergrid, each covering a list of ZIP codes.
- 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?”
- 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.
- We call it once more at checkout, so nobody orders something you can’t actually deliver.
- The order lands in your Offergrid dashboard and, if you want, as a signed webhook to your own system.
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.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.
The contract
Request
Offergrid sends a JSONPOST 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.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
Return200 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.Rules
An unserviceable address is a 200, not a 404
An unserviceable address is a 200, not a 404
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.Errors fail soft — but they cost you
Errors fail soft — but they cost you
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.Answer in under 2 seconds
Answer in under 2 seconds
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.
Be idempotent and side-effect free
Be idempotent and side-effect free
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.
Keep `serviceable` and `products` consistent
Keep `serviceable` and `products` consistent
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:
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.
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
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 Integrations → New 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: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 theserviceableboolean 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 ourline1as youraddress1. Constants your API requires (a partner ID, a promo code) are declared inline:"clientName": { "const": "offergrid" }. - No
serviceableboolean? PointonNetWhenAnyNonEmptyat the arrays that imply availability, e.g.["products", "fixedWirelessProducts"]— on-net when any is non-empty. - Products nested elsewhere?
products.pathis a dot-path:"data.availablePlans"works.keyPathcan 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".
What’s automated today
So you can plan operations around what exists rather than what’s described above in the abstract.Partial — checkout re-verification runs on storefront orders
Partial — checkout re-verification runs on storefront orders
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.
Manual — your secret is installed by us
Manual — your secret is installed by us
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.
Not yet — order handoff is notification, not order entry
Not yet — order handoff is notification, not order entry
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.
Internet first — inline coverage setup
Internet first — inline coverage setup
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 JSONUnserviceable addresses return
200 with serviceable: false and no productsEvery product carries a stable
key that won’t changePrices 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