Skip to content

Gate a checkout with the shipping policy

published · September 17, 2026 · updated · September 23, 2026

One boolean per address, decided by rules you set once: which signals block, how high the score may go, whether a register must confirm the building.

A shipping policy is the set of rules an account keeps about which address answers it will ship to. Every address lookup ends with ok_to_ship, the boolean those rules produce for that answer, and blocked_by, the name of the rule that decided a refusal. The score stays arithmetic and the flags stay facts; the policy is where a decision is made, and it is made once, on the server, so a checkout, a batch job, a bulk run and a monitored list all branch the same way.

Why a boolean, when the answer has 61 fields

Because the fields are evidence and a checkout needs a decision. A mail drop at 50 is a fact about a place; whether a parcel may go to a mailbox shop is a decision only the merchant can make, and a software vendor selling gift cards will make it differently from one selling furniture. A team that reads the fields ends up with an if-chain that grows with every new signal and drifts between the checkout, the import script and the fulfilment filter. A policy puts the chain in one place, applies it to every channel, and leaves blocked_by in the answer so the reasoning is visible to the person who has to explain a refusal.

Three rules make up a policy, all optional.

Rule What it does
block_signals A list of risk-signal names. Any answer whose risk_signals carries one of them is blocked, whatever its score.
max_risk_score A ceiling. An answer whose risk_score is above it is blocked.
require_exists Whether a national register must have confirmed the building. When true, an answer whose exists is anything but true is blocked.

They are applied in a fixed order after the answer is assembled: an address that cannot stand as written is blocked first and always, then the first blocked signal in the published weights' order, then the score ceiling, then the register requirement. blocked_by names the first rule that fired: invalid, the signal's own name, risk_score or unconfirmed. An address on your suppression list, served from the list without a lookup, answers suppressed.

The defaults

Until an account sets a policy, the published defaults apply. They block three signals — undeliverable, which only a licensed partner can raise; placeholder, the "asdf" and "no address" of a form filled to get past the form; and vacant, again from a partner — and any score over 60, and they require no register. A post-office box at 30, a terminated postcode at 30, a town that matches nowhere in its postcode area at 30 and a mail drop at 50 all pass by default; a private mailbox at 60 passes too, because the ceiling is exclusive, and a private mailbox with a town mismatch, at 90, does not.

Two of the defaults deserve a word. mail_drop is not blocked by default because a mailbox shop is a legitimate delivery point for a great many people — expatriates, people who travel, small businesses without an office — and a shop that sells physical goods to them would lose customers for nothing. And nothing about the register is required by default because exists is only ever true in the countries where a register of buildings is installed and complete; a policy that required it would refuse every British address, where the open register lists streets and never buildings, and every address in a country with no register at all.

The defaults are what the guest demo and the sandbox key answer with, because neither has an account to keep a policy. The five test addresses answer under the account's stored policy, like every other lookup a key makes, which makes them a way to try a policy before a customer meets it: the mail-drop test address, 2 Spaw Test Street, Springfield, IL 99999, answers ok_to_ship: true under the defaults and blocked_by: "mail_drop" on an account that blocks mail_drop. Under the defaults, "1 asdf, Los Angeles, CA 90028" answers ok_to_ship: false with blocked_by: "placeholder".

{
  "valid": true,
  "looks_placeholder": true,
  "risk_score": 60,
  "risk_level": "high",
  "ok_to_ship": false,
  "blocked_by": "placeholder"
}

Setting a policy

The policy belongs to the account and applies to every channel the account is billed for: the single and batch endpoints, bulk runs, monitored lists, the browser endpoint and the dashboard. Read it with GET /api/v1/address/policy, change it with PUT, and restore the defaults with DELETE. All three are free and none is logged; a key without the address scope is refused with KEY_SCOPE_DENIED.

curl -X PUT https://spaw.co/api/v1/address/policy \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"max_risk_score": 40, "block_signals": ["undeliverable", "placeholder", "vacant", "mail_drop", "unit_unexpected"], "require_exists": false}'

Send only the keys you are changing. A null clears the ceiling, an empty list blocks on no signal, a signal name the weights table does not carry is dropped, and a body naming none of the three rules answers 422, because that is the shape a misspelled key takes. The answer carries the policy in force, is_default, and when it was last changed. The Shipping policy tab on the verification page edits the same three rules with checkboxes, listing every signal beside its weight.

What a checkout should block

A checkout is the gate where a false refusal costs a sale and a false acceptance costs a parcel. Three policies cover most cases.

A shop shipping physical goods to consumers. Keep the defaults and add unit_unexpected: a suite number written at a building the register lists as a single residence is the invented-flat tell, and a parcel addressed to "Apt 3" at a house with no flats comes back. Leave mail_drop alone unless your fraud losses say otherwise, and leave unit_missing alone — a block of flats with no flat number is a delivery problem your carrier will raise with the customer, not a fraud signal.

A service with a free trial or a physical welcome kit. Add mail_drop and private_mailbox to the blocked signals and lower the ceiling to 40. A registered-agent address answers mail_drop_source: registered_agent and is blocked by the same signal; a company whose registered office is an agent's is ordinary, but nobody lives there, and a welcome kit sent there is a welcome kit sent to nobody.

A regulated onboarding that needs a residential address. Set require_exists to true — but only for the countries where a complete register of buildings is installed, which the country pages list, and only if you are prepared to hold every address from anywhere else for a manual look, because unconfirmed is what every one of them will answer. The honest way to do this is a policy per market, and the policy is per account, so most onboarding flows are better served by reading exists and register_coverage themselves and keeping the account policy for the signals.

None of these should block not_in_register without thought. It fires only under a register whose publisher claims every address, and those publishers are good, but a new building takes a quarter to reach a quarterly register, and a customer in a house finished last month is exactly who you would be refusing.

Using it in the flow

The browser endpoint verifies as the visitor types, with a publishable key and, if you turn it on, Cloudflare Turnstile in front of the lookup so bots cannot spend your credits. It answers the same ok_to_ship and blocked_by as the secret-key endpoint, under the same account policy, and it never blocks a submit on its own; the decision is yours to wire, and blocked_by is the word to show the customer or the fraud desk. Bulk runs carry both fields as CSV columns and count the blocked rows in the run's summary, and a monitored list re-applies the policy in force on the day it runs, so a policy tightened on Monday is what Saturday's run uses.

Related

markdown version: /guides/gate-a-checkout-with-the-shipping-policy.md

Verify addresses the same way

The Spaw API runs every check described here on each lookup, with an SMTP handshake that never sends mail. 10 free lookups a month, no card required.

Get your API key

More guides