Migrating from 2025-10 to 2026-05
Start with the cross-cutting changes; they affect every call. Then work through the per-resource sections for the resources your integration uses.
Cross-cutting changes
These changes affect every endpoint regardless of resource.
Request and response envelope
The flat top-level JSON objects of 2026-05 replace the 2025-10 JSON:API
envelope (data.type / data.attributes). Request bodies now send Content-Type: application/json rather than the JSON:API application/vnd.api+json.
Version header
Update the required AndOpen-API-Version header from 2025-10 to 2026-05. In
2026-05 a missing or unrecognized version is rejected with 400 and an
unsupported_api_version error, before the token is checked. See
Versioning.
Pagination
2025-10 paginated lists with a JSON:API links object and an opaque next URL.
2026-05 replaces this with keyset cursors: a list returns a data array and a
boolean has_more, and you fetch the next page by passing the id of the last
item as the after query parameter. See Pagination
for the full model.
Errors
Both versions return an errors array, but the entries change shape. 2025-10
followed JSON:API: each entry carried status and detail, alongside a top-level
jsonapi object. 2026-05 drops the jsonapi object, and each entry now carries
type, code, message, and param. The shape is identical across every
resource. The full type/code taxonomy and its HTTP-status mapping lives on the
Errors page; this section covers only what changed.
One authentication detail catches integrations that branched on the old codes:
2026-05 returns a single unauthorized code for every 401. A missing header,
an unrecognized token, an expired token, and a token presented to the wrong
region are no longer told apart. Collapse any branch on missing_token,
invalid_token, or expired_token into one path.
New endpoints
2026-05 adds list endpoints alongside the reads that already existed, plus a users resource that 2025-10 had no equivalent for:
Nothing in an existing integration has to change to adopt them.
Gift requests
Both gift-request endpoints, Create a gift request and Get a gift request, return the new response shape. Create additionally changes its request body, and gains idempotency.
Flat response object
2026-05 returns a gift request as a flat top-level JSON object; read each field
directly off the response. This replaces the 2025-10 JSON:API envelope, where
the resource sat under data across data.type, data.id, and
data.attributes.
// 2026-05
{ "id": "…", "status": "submitted", "redemption_url": "…", "recipient": { … } }
// 2025-10
{ "data": { "type": "gift_requests", "id": "…", "attributes": { "state": "submitted", "redemption_url": "…" } } }
Access fields directly: giftRequest.status in place of
giftRequest.data.attributes.state.
state is now status
The lifecycle field is status, a top-level field, previously state under
attributes. The values are unchanged: submitted, redeemed, dispatched,
delivered, cancelled.
New response fields
The 2026-05 gift request response carries data that 2025-10 omitted entirely. A
2025-10 gift request returned only state (now status), redemption_url, and
timestamps; everything below is new.
2026-05 embeds the owned associations (recipient, shipping_address,
offering, and line_items) directly as nested objects, so a single read
returns the whole gift request. Three are nullable, and the null carries
meaning:
shipping_addressisnullfor choice-mode gift requests and populated for direct-send ones.line_itemsisnull(never[]) until the items are locked: at creation for direct-send, at redemption for choice mode.offering.line_itemsisnullfor voucher, cause, and gift-set offerings, which 2026-05 does not represent yet.
offering describes what was put in front of the recipient: the items on offer,
whether they receive all of them or choose one, and whether they see them at
redemption. It is disjoint from the top-level line_items, so before redemption
offering.line_items holds the whole set and line_items is null, and after
redemption line_items holds what the recipient chose while
offering.line_items holds what they left.
Five further fields are new and always present:
referenceis a human-readable handle, unique within the account and stable for the gift request's lifetime.campaignis the campaign, embedded as an{ id, name, web_url }summary, ornullfor an ad-hoc send. Fetch the rest with Get a campaign.senderis the user responsible for the send, embedded as an{ id, name, email }summary. Read their team and budget with Get a user.web_urlis the gift request's sender-facing page in the &Open web app, on your organization's own domain. It needs a signed-in session, so it is not a substitute forredemption_url, which stays the recipient's own link.email_messageis the personal note that went into the invitation email, ornullwhen the send carries none.
The recipient object also gains name, which joins the first and last name,
and thank_you_count.
delivered covers partial fulfillment
The status values are unchanged, but delivered means something wider than a
2025-10 integration may assume. It covers a request whose gift reached the
recipient with remaining items failed or cancelled, and it is not terminal: the
status can change again while a remaining item is retried. Code that treats
delivered as the end of the lifecycle, or stops polling on it, needs revising.
Create request body
The create request flattens too, but watch the recipient. 2025-10 wrapped the
fields in a data.attributes envelope with the recipient's fields alongside
campaign_id; 2026-05 takes a flat body with campaign_id at the top level and
the recipient as a nested recipient object.
// 2026-05
{ "campaign_id": "…", "recipient": { "first_name": "…", "last_name": "…", "email": "…" } }
// 2025-10
{ "data": { "type": "gift_requests", "attributes": { "campaign_id": "…", "first_name": "…", "last_name": "…", "email": "…" } } }
The body also gains two optional fields 2025-10 had no equivalent for:
invitation_method, which overrides the campaign's configured method for one
send, and email_message, the personal note for the invitation email.
See Create a gift request for the full request shape.
Idempotency
Creating a gift request is now safe to retry, which 2025-10 offered no way to do. Two mechanisms apply, and the implicit one changes behavior you may be relying on today.
Send an Idempotency-Key header and a repeat within 24 hours returns the
original gift request, carrying an Idempotent-Replayed: true header. Reusing a
key with a different body returns a 422 with idempotency_key_mismatch.
Without a key, retries are still matched for 24 hours on campaign_id and
recipient.email. That protects a double submit, and it also means a second,
deliberately different gift to the same person on the same campaign is now
rejected with idempotency_conflict where 2025-10 would have created it. If
your integration sends more than one gift per recipient per campaign, supply an
Idempotency-Key on each.
Both mechanisms add a 409 with request_in_progress, returned when an
identical request is still being processed. Retry it unchanged after the
interval in the Retry-After header.
Campaigns
Both campaign endpoints, List campaigns and Get a campaign, change shape in 2026-05: a new response envelope, a different nested gift-collection representation, and a new pagination model on the list.
Flat object, no JSON:API envelope
In 2025-10 a campaign was a JSON:API resource: data.type ("campaigns"),
data.id, and the fields nested under data.attributes. In 2026-05 a campaign
is a flat top-level object: id, name, status, and the rest sit at the top
level, with no type or attributes wrapper.
state is now status
The state attribute is renamed to status. Its two public values are
unchanged: available and archived.
Gift collection is inline, not in included, and include is gone
This is the largest change. In 2025-10 the nested gift collection was returned through
JSON:API compound documents: the campaign held relationships with resource
identifiers, the actual product / variant / stock-level / warehouse records lived
in a sibling included array, and you opted into them with the
include=products.variants.variant_stock_levels.warehouse query parameter.
In 2026-05 the gift collection is always returned inline and fully nested, with no opt-in:
productsis an array directly on the campaign.- each product carries its
variantsinline, - each variant carries its
stock_levelsinline (renamed fromvariant_stock_levels), - and each stock level carries its
warehouseinline.
Two knock-on renames to watch for: the variant_stock_levels relationship is
now the stock_levels array, and a stock level's available count is now
quantity.
Because the tree is always inline, the include query parameter no longer
exists; remove it from your requests. Which products, variants, and stock levels
appear is instead governed by the campaign's fulfillment warehouse. When one is
set the gift collection is filtered to it (and each variant exposes a single,
warehouse-scoped stock level); campaigns without a warehouse return the
gift collection unfiltered. See Get a campaign for the detail.
New campaign fields
The campaign response carries seven fields 2025-10 had no equivalent for, all always present:
owneris the user who owns the campaign, embedded as an{ id, name, email }summary, ornullwhen no user owns it.web_urlis the campaign's page in the &Open web app, on your organization's own domain, and needs a signed-in session.offeringis how the campaign puts its gifts to a recipient, asselection_modeandvisible_at_redemption. It reflects the campaign's configured mode; a gift request's ownofferingis what happened on that send.supported_country_codeslists where the campaign ships to, andshipping_fromwhere it ships from, both as ISO 3166-1 alpha-2 codes. Each is an empty array when the campaign has no resolvable answer.default_email_messageis the campaign's default invitation copy, assubjectandbody.gift_request_statisticsrolls up the campaign's sends intototal,redeemed,dispatched, anddeliveredcounts.
Products gain an images array, and variants gain a price money object. Both
were absent in 2025-10, so nothing in an existing integration reads them yet.
See Get a campaign for the per-field detail.
List pagination and filters
The campaign list moves to the cursor model described under Pagination
above. Two campaigns-specific points: the page is capped at 25 (limit default and
maximum 25, because each item carries its full nested gift collection), and the list gains
sort plus filters on status, name, owner, owner.name, owner.email,
team, supported_country_codes, and a created_at range. See
List campaigns.
Webhooks
Webhook deliveries change shape the same way responses do, so update your handler before you point it at 2026-05.
For the payload, 2025-10 delivered a JSON:API document; 2026-05 delivers a flat JSON
object in the same shape the API returns for that resource. Parse it like any
other 2026-05 response. The delivery's Content-Type is now application/json, not
application/vnd.api+json.
2026-05 also adds two new headers, AndOpen-Webhook-Event-Type and
AndOpen-Webhook-API-Version, and exposes the event ID as an
AndOpen-Webhook-Event-Id header (in 2025-10 it was carried in the payload).
The signature and replay-protection scheme is unchanged, so existing
verification code keeps working.
See Webhooks for the full payload and verification details.