Skip to main content
Version: 2026-05

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_address is null for choice-mode gift requests and populated for direct-send ones.
  • line_items is null (never []) until the items are locked: at creation for direct-send, at redemption for choice mode.
  • offering.line_items is null for 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:

  • reference is a human-readable handle, unique within the account and stable for the gift request's lifetime.
  • campaign is the campaign, embedded as an { id, name, web_url } summary, or null for an ad-hoc send. Fetch the rest with Get a campaign.
  • sender is the user responsible for the send, embedded as an { id, name, email } summary. Read their team and budget with Get a user.
  • web_url is 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 for redemption_url, which stays the recipient's own link.
  • email_message is the personal note that went into the invitation email, or null when 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:

  • products is an array directly on the campaign.
  • each product carries its variants inline,
  • each variant carries its stock_levels inline (renamed from variant_stock_levels),
  • and each stock level carries its warehouse inline.

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:

  • owner is the user who owns the campaign, embedded as an { id, name, email } summary, or null when no user owns it.
  • web_url is the campaign's page in the &Open web app, on your organization's own domain, and needs a signed-in session.
  • offering is how the campaign puts its gifts to a recipient, as selection_mode and visible_at_redemption. It reflects the campaign's configured mode; a gift request's own offering is what happened on that send.
  • supported_country_codes lists where the campaign ships to, and shipping_from where 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_message is the campaign's default invitation copy, as subject and body.
  • gift_request_statistics rolls up the campaign's sends into total, redeemed, dispatched, and delivered counts.

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.