Skip to main content
Version: 2026-05

List campaigns

GET /campaigns

Description

Lists the campaigns belonging to your account, newest first, as a cursor-paginated collection. A campaign is the gift collection offered on a send. It groups the products a recipient can be given, and it's the thing you reference from Create a gift request to drive what gets offered and how.

Each item in the list carries the full campaign shape: the same object Get a campaign returns, including its product, variant, stock-level, and warehouse details.

When to use

Use this endpoint to discover which campaigns exist, for example to present a picker, or to find the id you'll pass as campaign_id when you create a gift request. When you already know the campaign you want and only need that one, fetch it directly with Get a campaign rather than paging the whole list.

Parameters

Headers

AndOpen-API-Version (string · required) is the API version this request targets. Always send 2026-05. Requests authenticate with a bearer token; see Authentication for how to present it and Environments for the regional base URL to send it to.

Query parameters

Every one of these is optional, and sent together they page, sort, and filter the same collection.

Paging

limit (integer · optional) is how many campaigns to return per page. Defaults to 25, which is also the maximum: each campaign carries its full nested gift collection, so the page is capped to keep response size and time predictable. A larger value is clamped to 25 server-side rather than rejected; a non-numeric, zero, or negative value is rejected with a 400.

after (string · uuid · optional) is the keyset pagination cursor. Pagination is cursor-based, not page-numbered: to fetch the next page, pass the id of the last campaign in the page you just received. Omit it for the first page. A cursor that references no existing record is rejected with a 400. Stop paging when the response's has_more is false.

Ordering

sort (string · optional) is the field to order by, - prefix for descending. Defaults to -created_at (newest first); created_at, name, -name, updated_at, and -updated_at are also accepted. Any other field is rejected with a 400.

Filtering

Filters combine with AND, so each one you add narrows the page further. Two kinds behave differently when nothing matches, and the difference is worth knowing before you debug an empty page:

  • An exact-match filter (status, owner, team, supported_country_codes) given an unrecognized value matches nothing and returns an empty page.
  • A substring filter (name, owner.name, owner.email) is case-insensitive and matches anywhere in the value.

status (string · optional) filters by public status. Accepts a single value (available or archived) or a comma-separated list of both.

name (string · optional) matches campaigns whose name contains the value. Values longer than 255 characters are truncated.

owner (string · optional) filters by the owning user's id. The literal me resolves to the authenticated user, which needs user authentication: a service token has no connecting user and gets a 400.

owner.name (string · optional) and owner.email (string · optional) match against the owner instead of the campaign. owner.email is a substring match, so a bare domain such as @example.com selects everyone on it.

team (string · optional) filters by the owning user's team. Accepts a single UUID or a comma-separated list.

supported_country_codes (string · optional) filters by shipping destination. Accepts a single ISO 3166-1 alpha-2 code or a comma-separated list, and matches a campaign that supports any of them.

created_at (object · optional) filters by creation time with bracketed range operators: created_at[gte], created_at[lte], created_at[gt], and created_at[lt]. Values are ISO 8601 timestamps, and several operators combine into an interval, so created_at[gte]=2026-01-01T00:00:00.000Z&created_at[lt]=2026-04-01T00:00:00.000Z is the first quarter. An unknown operator or a non-ISO-8601 value is rejected with a 400.

Worked examples

The cURL version is the raw HTTP call; the JavaScript, Python, and Ruby versions are idiomatic equivalents. Each requests the first page with the defaults.

curl -X GET "https://api.andopen.co/campaigns" \
-H "Authorization: Bearer <api_key>" \
-H "AndOpen-API-Version: 2026-05"

Paging through every campaign

To walk the whole collection, keep fetching while has_more is true, passing the last campaign's id as the after cursor each time. The cURL version shows a single follow-up request; the others loop until the last page.

curl -X GET "https://api.andopen.co/campaigns?after=a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
-H "Authorization: Bearer <api_key>" \
-H "AndOpen-API-Version: 2026-05"

Response shape

A successful call returns 200 OK with a page object: a data array of campaigns plus a boolean cursor flag. Paging is driven by has_more and the after cursor.

FieldDescription
data (array · required)The campaigns in this page. Each entry is a full campaign object, identical to what Get a campaign returns; see that page for the per-field detail of the nested gift collection.
has_more (boolean · required)Whether more campaigns exist beyond this page. When true, fetch the next page by passing the last item's id as the after cursor; when false, you've reached the end.
data.status (string · required)Each campaign's status, one of available, archived. data.products (array · required) is its inline gift-collection tree.
data.owner (object · required · nullable)Each campaign's owner summary. supported_country_codes and gift_request_statistics come inline too, so a picker can show who owns a campaign, where it ships, and how much it has been used without a second call.
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Q2 Customer Appreciation",
"description": "Send a thank-you gift to our top accounts.",
"status": "available",
"web_url": "https://acme.andopen.co/sender/gift-history/campaigns/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"default_email_message": {
"subject": "A gift for you",
"body": "Thanks for being a valued customer. Enjoy your gift!"
},
"owner": {
"id": "9f8e7d6c-5b4a-3210-fedc-ba9876543210",
"name": "Dana Okafor",
"email": "dana@acme.example"
},
"supported_country_codes": [
"IE",
"GB",
"US"
],
"shipping_from": [
"IE"
],
"gift_request_statistics": {
"total": 120,
"redeemed": 84,
"dispatched": 12,
"delivered": 70
},
"offering": {
"selection_mode": "one",
"visible_at_redemption": true
},
"archived_at": null,
"products": [
{
"id": "b2c3d4e5-f6a7-8901-bcde-f01234567890",
"name": "Leather Notebook",
"enabled": true,
"images": [
{
"name": "leather-notebook.jpg",
"url": "https://files.andopen.co/rndd63mwgj94j2vf3v2ngnye0hkn",
"urls": {
"thumbnail": "https://files.andopen.co/vw2mvwevyqxh98l7esf67nvunr4h",
"large": "https://files.andopen.co/mz4rc8kx1nvq7j93wtf5bd2hs0yl",
"large_wide": "https://files.andopen.co/q7pz3n8htv2ke0d5rjw94flxs6cb"
}
}
],
"variants": [
{
"id": "c3d4e5f6-a7b8-9012-cdef-012345678901",
"name": "Medium / Black",
"sku": "NB-LTHR-MED-BLK",
"enabled": true,
"position": 1,
"price": {
"amount": 1500,
"currency": "EUR"
},
"stock_levels": [
{
"id": "d4e5f6a7-b8c9-0123-defa-123456789012",
"quantity": 42,
"warehouse": {
"id": "e5f6a7b8-c901-2345-efab-234567890123",
"name": "EU Central — Frankfurt",
"enabled": true,
"created_at": "2026-04-16T10:30:00.000Z",
"updated_at": "2026-04-16T10:30:00.000Z"
},
"created_at": "2026-04-16T10:30:00.000Z",
"updated_at": "2026-04-16T10:30:00.000Z"
}
],
"created_at": "2026-04-16T10:30:00.000Z",
"updated_at": "2026-04-16T10:30:00.000Z"
}
],
"created_at": "2026-04-16T10:30:00.000Z",
"updated_at": "2026-04-16T10:30:00.000Z"
}
],
"created_at": "2026-04-16T10:30:00.000Z",
"updated_at": "2026-04-16T10:30:00.000Z"
}
],
"has_more": true
}

Per-field notes

  • Every item is the full campaign. The list does not return a trimmed summary; each entry includes the same product, variant, stock-level, and warehouse details as the single-campaign endpoint. This is why the page size is capped at 25.
  • has_more drives paging, not a total count. The response carries no total and no next URL. Treat has_more: false as the only signal to stop, and build the next request from the last item's id.
  • Exact-match filtering is forgiving. An unknown value for status, owner, team, or supported_country_codes yields an empty page, not a 400, so an empty data array can mean either "no matches" or "filter typo". The gift-collection tree within each campaign follows the same warehouse-filtering rules described on Get a campaign.

Error cases

Every failure uses the shared error model: a top-level errors array of objects carrying type, code, message, and (for field-level problems) param. See Errors for the full type/code taxonomy and how to handle each category; the codes below are the ones this endpoint produces.

StatusMeaning
400The request is malformed or violates a precondition. Typical triggers are a missing or unsupported `AndOpen-API-Version` header, a request body that is not valid JSON, or pagination parameters that fail server-side validation (`limit` non-numeric or non-positive, `after` referencing a record that does not exist).
401Authentication failed: missing or invalid bearer token.
403Authorized but not permitted to access this resource.
429Too many requests. Rate limit exceeded.
500Internal server error.
503Service unavailable. The account may be under maintenance. Check the `Retry-After` header.

Every error carries a type, one of: validation_error, authentication_error, authorization_error, not_found_error, rate_limit_error, api_error.

The ones you'll meet in practice:

  • 400 validation_error: a query parameter was rejected, each naming the offending one in param. An after cursor referencing no existing record is invalid_reference. A limit that is not a positive integer, an unsupported sort field, a malformed created_at operator or timestamp, and owner=me under service authentication are all invalid_format.
  • 400 api_error: unsupported_api_version when the AndOpen-API-Version header is missing or unsupported (returned before authentication).
  • 401 authentication_error: the bearer token is missing, invalid, or expired (unauthorized).
  • 403 authorization_error: the token is valid but not permitted to list campaigns (forbidden).
  • 429 rate_limit_error: too many requests; back off and retry (rate_limited). The RateLimit-* response headers tell you the budget and reset time.