# Gate a checkout with the shipping policy

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.

Date: 2026-09-17

Updated: 2026-09-23

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"`.

```json
{
  "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`.

```bash
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.

Reference: https://spaw.co/guides/gate-a-checkout-with-the-shipping-policy
