Skip to main content
A buyer profile is the answer to “who is this order for and where does it go”. The agent reads it when it reaches the merchant’s contact and shipping form, so you supply it once instead of re-typing an address into every checkout. Pass buyerProfileId when creating a checkout. Without one the agent has no address, and on most merchants that is where the run stops.

Create

Response (201)

Fields

Every field is optional — the API will happily store {} — but the agent can only fill what you gave it. A field it does not recognise is a different matter: the body is strict, so a typo is a 400 rather than a stored profile missing the thing you thought you set.
string
Yours, for your own bookkeeping. Never sent to a merchant.
string
Your own id for the end user this profile is. Optional, 1 to 200 characters, opaque to us, never sent to a merchant. Set it and an API key bound to that subject sees this profile while no other end user’s key can — see End-user isolation.It comes back as null on an untagged profile, and it cannot be changed after creation: PATCH with subject is a 400, not a silent no-op. Decide before you create. "" is a 400 as well — send a real id or omit the field.
object
{ first, last }. Used for the merchant’s name fields, and as the cardholder name at the payment step — which is why the card action never asks for one.
object
{ email, phone }. email is what order confirmations go to; most checkouts refuse to proceed without it. phone is required by a fair number of merchants, particularly delivery.
object
{ addressLines, locality, administrativeAreaCode, postalCode, countryCode }.
  • addressLines — array of strings. Only the first line is filled on the Shopify tier; a second line is stored but will not be typed. If an apartment number matters, put it on line one.
  • locality — city / town.
  • administrativeAreaCode — state / province. Send the short code (KY, ON) — the code is tried first, then the full name as a visible label.
  • postalCode
  • countryCode — ISO 3166-1 alpha-2. Filled before the state, because changing country repopulates the province list and would wipe a state chosen first.
For US and CA addresses, administrativeAreaCode is effectively required. Shopify will not offer a shipping method until the state or province is set, and with no shipping method the order can never be placed — the run fails with Could not reach the payment step and the real cause is three steps upstream.
Values are stored as sent. No normalisation, no address validation, no correction. If the postal code does not match the city, you find out when the merchant rejects it.

Read

List → 200
The list is cursor-paginated, newest first. Pass nextCursor back as ?cursor= for the next page; nextCursor: null means you have them all. limit defaults to 20 and caps at 100.

Update

The response is the full updated profile — the same shape as create.

PATCH replaces a document wholesale

This is the one thing to get right. name, contact and shipping are each stored as a single JSON document, and PATCH is partial at the top level only:
  • Omit a key and that document is left exactly as it was.
  • Supply a key and its document is replaced entirely. Fields you did not mention are gone, not inherited.
There is no deep merge, deliberately. Patching half an address and inheriting the other half from the old one is how you ship a parcel to a street in the wrong city — a new postalCode silently keeping the old locality produces an address that looks complete and is wrong.Send a whole shipping object or none at all.
Three smaller behaviours:
  • An empty body is a 400 No fields to update, not a no-op that quietly succeeds.
  • subject is rejected, not ignored — a 400 with an unrecognized_keys issue. A profile cannot be reassigned to a different end user, and a caller attempting it should be told so rather than getting a 200 and a profile that has not moved. See End-user isolation.
  • Send "name": null to clear a document outright — that is how you remove an address you no longer want us holding, short of deleting the whole profile. "name": {} stores an empty object instead, which is a different thing.
One consequence worth designing around: a checkout stores only buyerProfileId, never a copy of the address. Resolve that id after an update and you get the current address, not the one the parcel actually went to. If shipped-to addresses matter to you — and for anything with a refund or dispute process they do — record the address yourself at creation time rather than resolving the reference later.

Delete

Response (200)
A real delete, not a tombstone. This is the path for honouring a data-deletion request — “remove what you hold about me” has an answer, and it is this endpoint. What survives and what goes is decided by the schema, not by a cleanup job that might not run:

Checkouts survive

A checkout that referenced the profile keeps existing, with its buyerProfileId reference set to null. Purchase history outlives the identity, so your books still balance and the deleted person is no longer in them.

Buyer sessions cascade

Stored merchant logins for that profile are deleted with it. That is the point of deleting, not a side effect: leaving a live merchant session behind for a buyer you just erased is the opposite of what was asked for.
Deleting is not reversible and there is no soft-delete flag to undo. 404 Profile not found covers both “no such id” and “another tenant’s” — a second delete of the same profile is a 404, so treat that as success if you are retrying.
A run currently in flight against a deleted profile does not stop. It already loaded the address it needed; the reference simply nulls out. Cancel the checkout too if the point is that nothing more should be shipped.

Profiles and stored merchant sessions

A buyer profile is also the identity that buyer sessions hang off. When a run logs into a merchant with credentials you supplied, the resulting cookies are stored against (tenant, buyerProfileId, merchantDomain) — so the next checkout for that profile at that merchant starts logged in. That means the profile id is not just an address book entry. Reusing one profile across unrelated end users would share their merchant logins. One profile per end user. If you serve many end users, say which one each profile belongs to: set subject at creation and issue your agent a key bound to it. That turns “one profile per end user” from a convention you maintain into a fence the API enforces — a key for one buyer cannot read, patch, delete or ship to another’s profile, and cannot list them either. The fence holds from your admin key too, which is where it matters for stored logins: a checkout tagged subject: "user_a91f" cannot be pointed at a profile tagged for someone else, so a run can never write one person’s merchant session onto another person’s profile. Naming both is a 403. See End-user isolation.