Create or update the customer
checkoutCustomerUpsert creates a customer profile, or updates it if one already exists for that customerId. Unlike checkoutCustomerProfileAuthenticate, this does not require the shopper to be present, so you can provision your customer list ahead of time. See create or update a profile without a shopper for the full field behavior.
mutation checkoutCustomerUpsert($input: CheckoutCustomerProfileInput!) { checkoutCustomerUpsert(input: $input) { customerId email name phone }}Because it matches on customerId, calling it repeatedly updates the same profile rather than creating duplicates. It is safe to run across your whole customer list on every sync.
Sync tax exemptions
checkoutCustomerTaxExemptionsSync accepts several customers at once. Each entry replaces that customer's full set of exemptions.
mutation checkoutCustomerTaxExemptionsSync($input: [CheckoutCustomerTaxExemptionSyncInput!]!) { checkoutCustomerTaxExemptionsSync(input: $input) { customerId accepted { id countryCode administrativeArea effectiveAt expiresAt exemptionReason } rejected { code message administrativeArea effectiveAt } }}An omitted expiresAt means the certificate does not expire. An omitted exemptionReason defaults to UNSPECIFIED.
How syncing works
Three rules govern every sync.
The list is authoritative. taxExemptions is the customer's complete set, not a list of changes. Any exemption already on record that is missing from the list is removed — that is how a withdrawn certificate stops being honored. Sending a partial list silently removes everything it leaves out.
Every record is an exemption. There is no way to record that a customer is not exempt somewhere — see the note below.
Removing all exemptions is an empty list. To clear a customer's certificates, send them with "taxExemptions": []. Leaving the customer out of the payload entirely leaves their existing records untouched.
Exemption reasons
exemptionReason describes why the customer is exempt. It is optional and defaults to UNSPECIFIED, but supplying it is worth the effort — see below.
| Value↕ | Applies to↕ |
|---|---|
RESALE | Goods purchased to resell rather than consume |
FEDERAL_GOVERNMENT | A US federal agency or department |
STATE_LOCAL_GOVERNMENT | A state agency, county, municipality, or school district |
TRIBAL_GOVERNMENT | A federally recognized tribe or tribal member |
CHARITABLE | A charitable nonprofit |
RELIGIOUS_ORGANIZATION | A church or other religious organization |
EDUCATIONAL_ORGANIZATION | A school or university |
DIRECT_PAY | A buyer who holds a direct pay permit and remits tax themselves |
OTHER | Anything else, including foreign diplomat, agricultural production, industrial production, and direct mail |
UNSPECIFIED | No reason supplied |
Reasons fall into two groups, and the difference matters. Entity-based reasons — government, charitable, religious, educational — exempt the buyer no matter what they purchase. Use-based reasons, RESALE above all, only cover qualifying goods: a resale certificate covers inventory the buyer will resell, not the office furniture on the same order.
Without a reason, Zonos cannot tell the two apart and can only exempt whole orders, which is hardest to defend for resale certificates. If most of your exemptions are resale, sending RESALE as a default with exceptions listed individually is usually much less work than classifying every customer.
Exemption reference
exemptionReference is the number printed on the customer's exemption paperwork. Depending on the state and the type of exemption, that may be a resale or seller's permit number, a state exemption certificate number, a direct pay permit number, or a federal tax ID.
It is required on every record. Store and send it exactly as it appears on the certificate — formats vary widely by jurisdiction (SR EAA 12-345678, 85-8012345678C-9, 12-3456789), and Zonos preserves the value as sent apart from trimming surrounding whitespace. Do not change capitalization or strip hyphens and spaces.
Rejected records
Records are validated one at a time. A rejected record is reported in rejected and does not fail the rest of the batch, nor does it disturb the exemption already on record for that jurisdiction.
| Code↕ | Cause↕ |
|---|---|
UNKNOWN_CUSTOMER | No customer matches customerId. Create the customer first |
UNKNOWN_JURISDICTION | administrativeArea is not a recognized US state, district, or territory |
INVALID_DATE_RANGE | expiresAt is not after effectiveAt |
MISSING_EXEMPTION_REFERENCE | exemptionReference is empty |
DUPLICATE_JURISDICTION | Two records in one payload share a country, area, and effective date. The first is kept |
Unknown customers are rejected rather than created, so a mistyped customerId surfaces in the response instead of creating a profile that never matches a real buyer.
Only US administrative areas are validated. Subdivisions of other countries are accepted as sent.
Read back and remove
Read a customer's exemptions to confirm a sync landed:
query checkoutCustomerTaxExemptions($customerId: String!) { checkoutCustomerTaxExemptions(customerId: $customerId) { countryCode administrativeArea effectiveAt expiresAt exemptionReason }}To remove every exemption for a customer — when they close their account, for example — use checkoutCustomerTaxExemptionsDelete. It returns SUCCESS whether or not the customer had any exemptions, so it is safe to call more than once.
mutation checkoutCustomerTaxExemptionsDelete($customerId: String!) { checkoutCustomerTaxExemptionsDelete(customerId: $customerId)}
Tax exemptions
Sync US sales tax exemption certificates to a customer profile.
If you sell to resellers, government agencies, or nonprofits, those buyers hold exemption certificates that exempt them from sales tax in specific states. Zonos stores those certificates against a customer profile so an exempt buyer can be recognized at checkout.
Exemptions are recorded per jurisdiction. A customer exempt in South Carolina is not automatically exempt in Texas, so each state the customer holds a certificate for is a separate record with its own dates and certificate number.
Exemptions attach to an existing customer, so syncing is two calls: create the customer, then sync their certificates. Both are keyed on your own customer ID — the same
customerIdyou use elsewhere in Checkout. Zonos never requires you to store an internal ID.This feature is available for custom API integrations only.
You can sync and manage exemptions today, and the records are stored against the customer profile. Deducting an exemption from the tax on a landed cost is coming soon, so a synced exemption does not change the tax a shopper is quoted yet. Syncing now means your certificates are already in place when it does.