API ReferenceEndpointsStorefront donations

Storefront donations

Donation flows for a storefront: list the donation campaigns and their progress, read the funds and dedication types a donation form offers, start a donation (which returns a payment intent), read a donation back for a thank-you page, and email a donor a link to manage their monthly donations. Authenticated with the sales-channel id (vc_*) in the path and the browser Origin header.

Start a donation and mint a payment intent for it

Starts a donation and creates a payment intent for it. It is gated on the store switch for donations, not on the cart or order scopes. A clientRequestId makes a double click return the same donation. Limited to 5 requests per minute per IP.

POST
/vc/{connectionId}/donations
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Sales channel ID (vc_*), the canonical salesChannelId. connectionId is the deprecated name of the same value and is still the literal path segment.

Header Parameters

originstring

Origin of the storefront page making the call, for example https://shop.example.com. Required: a request with no Origin header is refused with 403. On a LIVE channel its host must equal the channel's registered domain (or one of its subdomains) or match an allowed origin. A TEST channel with no domain accepts any origin; once a domain is set it is matched as on LIVE.

X-SDK-Version?string

Version of the calling SDK (the Brainerce SDK sends it automatically). Informational: it is recorded on the channel and never changes the response.

amountnumber

The gift itself, in the store base currency. EXCLUDES feeCoverAmount — this is the figure the donor is credited with and the figure a receipt is written for.

Range0.01 <= value <= 1000000
feeCoverAmount?number

Processing fee the donor volunteered to absorb. Charged on top of amount, never subtracted from it and never a line item. Around 55-60% of donors accept this when offered.

Default0
Range0 <= value <= 1000000
donorEmailstring

The donor's email. Also how their giving history is found later.

Lengthlength <= 320
donorName?string

The donor's name, as they want it recorded.

Lengthlength <= 200
isAnonymous?boolean

Hides the donor name on public surfaces (a donor wall, a thank-you page). Does NOT anonymise the record — the merchant still sees the donor in their own reporting.

Defaultfalse
tributeType?string

Tribute gift. tributeName is required when this is set.

Value in"IN_HONOR" | "IN_MEMORY"
tributeName?string

Who the gift honours or remembers.

Lengthlength <= 200
message?string

The donor's own words. Stored and displayed as plain text, never as HTML.

Lengthlength <= 2000
salesChannelId?string

Which storefront the donation came through. Drives the return URL the payment provider sends the donor back to.

returnPath?string

Where to land the donor after payment, as a PATH on the sales channel’s own domain (e.g. /thank-you). A full URL is rejected: the provider redirects a browser here, so accepting one would make this an open redirect.

Match^\/(?!\/)[A-Za-z0-9\-._~!$&'()*+,;=:@%/?#[\]]*$
Lengthlength <= 512
campaignId?string

The donation campaign this gift is for (its id, from the public campaign read). Omit for a general gift. Refused unless the campaign is active and inside its dates, and the amount fits the campaign’s limits: 404 campaign_not_found, 409 campaign_not_started / campaign_paused / campaign_ended, 400 amount_below_minimum / amount_above_maximum / amount_not_offered.

Lengthlength <= 64
fundId?string

The fund (designation) the gift is for, from getDonationOptions(). Defaults to the campaign’s fund when giving to a campaign; the donor may choose another. 400 fund_not_available for an unknown or archived fund.

Lengthlength <= 64
dedicationTypeId?string

One of the store’s own dedication types, from getDonationOptions() (e.g. "For the recovery of"). Requires tributeName (the honoree). Use instead of tributeType, not with it. 400 dedication_not_available for an unknown or retired type.

Lengthlength <= 64
receiptName?string

The name the receipt is made out to, when it differs from the donor (a spouse, a company).

Lengthlength <= 200
donorPhone?string

Donor phone number, for the receipt and the organisation.

Match^[+0-9 ()-]{6,24}$
receiptTaxId?string

National ID or company number of the donor, for a tax receipt. Stored encrypted; never returned publicly, and the organisation sees only the last four characters.

Match^[0-9A-Za-z-]{4,20}$
publicName?string

The name the donor allows on public surfaces (a donor wall, a thank-you page). Ignored when isAnonymous is true; defaults to donorName.

Lengthlength <= 200
recurring?RecurringDonationRequestDto

Make this a monthly donation: this first gift is charged now and the card is saved for the following months. Refused before any charge with 409 recurring_not_supported when the store’s payment provider cannot charge a saved card, or 409 provider_mode_unknown when its live/test mode is not declared. A covered fee applies to this first gift only.

clientRequestId?string

One random id per donation-form attempt (e.g. crypto.randomUUID()), resent unchanged on every submit of that attempt. A double-click or a network retry with the same id returns the SAME donation and payment page instead of opening a second one. Reusing an id with different amounts, email, channel or return path is refused with 409 idempotency_key_mismatch; once an attempt has failed or ended, its id answers 409 donation_attempt_closed, so start the next attempt with a new id.

Match^[A-Za-z0-9_-]{16,100}$
curl -X POST "https://api.brainerce.com/api/vc/string/donations" \  -H "origin: string" \  -H "X-SDK-Version: string" \  -H "Content-Type: application/json" \  -d '{    "amount": 180,    "donorEmail": "[email protected]"  }'
{
  "donationId": "string",
  "status": "PENDING",
  "amount": "36.00",
  "feeCoverAmount": "0.00",
  "chargeAmount": "36.00",
  "currency": "USD",
  "payment": {
    "intentId": "string",
    "clientSecret": "string",
    "clientSdk": {},
    "redirectUrl": "string",
    "providerType": "string"
  }
}

{
  "statusCode": 400,
  "code": "VALIDATION_FAILED",
  "message": "name should not be empty, price must be a positive number",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donations"
}

{
  "statusCode": 403,
  "code": "INSUFFICIENT_SCOPE",
  "message": "API key does not have the required scope: products:write",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donations"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donations"
}

{
  "statusCode": 409,
  "code": "CONFLICT",
  "message": "The request conflicts with the current state of the resource",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donations"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donations"
}

{
  "statusCode": 503,
  "code": "INTERNAL_SERVER_ERROR",
  "message": "Internal server error",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donations"
}

Read a donation back for a thank-you page

Reads a donation back for a thank-you page. Limited to 5 requests per minute per IP, because an id that either resolves or 404s would be an enumeration oracle.

GET
/vc/{connectionId}/donations/{donationId}
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Sales channel ID (vc_*), the canonical salesChannelId. connectionId is the deprecated name of the same value and is still the literal path segment.

donationIdstring

Donation id, returned by donations.

Header Parameters

originstring

Origin of the storefront page making the call, for example https://shop.example.com. Required: a request with no Origin header is refused with 403. On a LIVE channel its host must equal the channel's registered domain (or one of its subdomains) or match an allowed origin. A TEST channel with no domain accepts any origin; once a domain is set it is matched as on LIVE.

X-SDK-Version?string

Version of the calling SDK (the Brainerce SDK sends it automatically). Informational: it is recorded on the channel and never changes the response.

curl -X GET "https://api.brainerce.com/api/vc/string/donations/string" \  -H "origin: string" \  -H "X-SDK-Version: string"
{
  "id": "string",
  "status": "PENDING",
  "amount": "36.00",
  "feeCoverAmount": "0.00",
  "currency": "USD",
  "donorName": "string",
  "tributeType": "IN_HONOR",
  "tributeName": "string",
  "dedicationLabel": "string",
  "monthly": true,
  "isTest": true,
  "paidAt": "2019-08-24T14:15:22Z"
}

{
  "statusCode": 403,
  "code": "INSUFFICIENT_SCOPE",
  "message": "API key does not have the required scope: products:write",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donations/{donationId}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donations/{donationId}"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donations/{donationId}"
}

List visible donation campaigns with their progress

Lists the visible donation campaigns (active and paused, at most 50, newest first) with their progress. Empty when the store has donations closed. Sent with Cache-Control: public, max-age=30.

GET
/vc/{connectionId}/donation-campaigns
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Sales channel ID (vc_*), the canonical salesChannelId. connectionId is the deprecated name of the same value and is still the literal path segment.

Query Parameters

locale?string

Language code, for example he. Used only when no Accept-Language header is sent. A language the store does not support, or the store's default language, returns the default text.

Header Parameters

originstring

Origin of the storefront page making the call, for example https://shop.example.com. Required: a request with no Origin header is refused with 403. On a LIVE channel its host must equal the channel's registered domain (or one of its subdomains) or match an allowed origin. A TEST channel with no domain accepts any origin; once a domain is set it is matched as on LIVE.

accept-language?string

Preferred language, for example he. Its first tag is used when it is one of the store's supported languages other than the default; translated fields are then returned in that language. Falls back to the locale query parameter.

X-SDK-Version?string

Version of the calling SDK (the Brainerce SDK sends it automatically). Informational: it is recorded on the channel and never changes the response.

curl -X GET "https://api.brainerce.com/api/vc/string/donation-campaigns?locale=string" \  -H "origin: string" \  -H "accept-language: string" \  -H "X-SDK-Version: string"
{
  "data": [
    {
      "id": "string",
      "slug": "building-fund",
      "name": "string",
      "description": "string",
      "image": "string",
      "state": "active",
      "fundId": "string",
      "startsAt": "2019-08-24T14:15:22Z",
      "endsAt": "2019-08-24T14:15:22Z",
      "currency": "USD",
      "goalAmount": "50000.00",
      "raisedAmount": "12450.00",
      "donationCount": 213,
      "percentOfGoal": 24,
      "suggestedAmounts": [
        "18.00",
        "36.00",
        "100.00"
      ],
      "allowCustomAmount": true,
      "minAmount": "string",
      "maxAmount": "string"
    }
  ]
}

{
  "statusCode": 403,
  "code": "INSUFFICIENT_SCOPE",
  "message": "API key does not have the required scope: products:write",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donation-campaigns"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donation-campaigns"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donation-campaigns"
}

Read one donation campaign by slug, with its progress

Returns one donation campaign by slug with its progress. Pass the returned id as campaignId to donations. Sent with Cache-Control: public, max-age=30.

GET
/vc/{connectionId}/donation-campaigns/{slug}
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Sales channel ID (vc_*), the canonical salesChannelId. connectionId is the deprecated name of the same value and is still the literal path segment.

slugstring

Campaign slug.

Query Parameters

locale?string

Language code, for example he. Used only when no Accept-Language header is sent. A language the store does not support, or the store's default language, returns the default text.

Header Parameters

originstring

Origin of the storefront page making the call, for example https://shop.example.com. Required: a request with no Origin header is refused with 403. On a LIVE channel its host must equal the channel's registered domain (or one of its subdomains) or match an allowed origin. A TEST channel with no domain accepts any origin; once a domain is set it is matched as on LIVE.

accept-language?string

Preferred language, for example he. Its first tag is used when it is one of the store's supported languages other than the default; translated fields are then returned in that language. Falls back to the locale query parameter.

X-SDK-Version?string

Version of the calling SDK (the Brainerce SDK sends it automatically). Informational: it is recorded on the channel and never changes the response.

curl -X GET "https://api.brainerce.com/api/vc/string/donation-campaigns/string?locale=string" \  -H "origin: string" \  -H "accept-language: string" \  -H "X-SDK-Version: string"
{
  "id": "string",
  "slug": "building-fund",
  "name": "string",
  "description": "string",
  "image": "string",
  "state": "active",
  "fundId": "string",
  "startsAt": "2019-08-24T14:15:22Z",
  "endsAt": "2019-08-24T14:15:22Z",
  "currency": "USD",
  "goalAmount": "50000.00",
  "raisedAmount": "12450.00",
  "donationCount": 213,
  "percentOfGoal": 24,
  "suggestedAmounts": [
    "18.00",
    "36.00",
    "100.00"
  ],
  "allowCustomAmount": true,
  "minAmount": "string",
  "maxAmount": "string"
}

{
  "statusCode": 403,
  "code": "INSUFFICIENT_SCOPE",
  "message": "API key does not have the required scope: products:write",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donation-campaigns/{slug}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donation-campaigns/{slug}"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donation-campaigns/{slug}"
}

The funds and dedication types a donation form offers

Returns what a donation form offers: the active funds and the dedication wording. Empty lists when the store offers none or has donations closed. Sent with Cache-Control: public, max-age=60.

GET
/vc/{connectionId}/donation-options
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Sales channel ID (vc_*), the canonical salesChannelId. connectionId is the deprecated name of the same value and is still the literal path segment.

Query Parameters

locale?string

Language code, for example he. Used only when no Accept-Language header is sent. A language the store does not support, or the store's default language, returns the default text.

Header Parameters

originstring

Origin of the storefront page making the call, for example https://shop.example.com. Required: a request with no Origin header is refused with 403. On a LIVE channel its host must equal the channel's registered domain (or one of its subdomains) or match an allowed origin. A TEST channel with no domain accepts any origin; once a domain is set it is matched as on LIVE.

accept-language?string

Preferred language, for example he. Its first tag is used when it is one of the store's supported languages other than the default; translated fields are then returned in that language. Falls back to the locale query parameter.

X-SDK-Version?string

Version of the calling SDK (the Brainerce SDK sends it automatically). Informational: it is recorded on the channel and never changes the response.

curl -X GET "https://api.brainerce.com/api/vc/string/donation-options?locale=string" \  -H "origin: string" \  -H "accept-language: string" \  -H "X-SDK-Version: string"
{
  "funds": [
    {
      "id": "string",
      "slug": "string",
      "name": "string",
      "description": "string"
    }
  ],
  "dedicationTypes": [
    {
      "id": "string",
      "label": "string"
    }
  ],
  "receiptTaxIdRequired": true
}

{
  "statusCode": 403,
  "code": "INSUFFICIENT_SCOPE",
  "message": "API key does not have the required scope: products:write",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donation-options"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donation-options"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donation-options"
}

Emails a donor a link to manage their monthly donations. Always answers 202 with the same body, whether or not the address donates monthly here, so it cannot be used to find donors. Limited to 3 requests per minute per IP.

POST
/vc/{connectionId}/donor-manage/request-link
X-Sales-Channel-Origin<token>

For /api/vc/{connectionId}/* routes the connectionId (vc_*) is in the URL path — not a header. Live-mode requests must also send a matching Origin header.

In: header

Path Parameters

connectionIdstring

Sales channel ID (vc_*), the canonical salesChannelId. connectionId is the deprecated name of the same value and is still the literal path segment.

Header Parameters

originstring

Origin of the storefront page making the call, for example https://shop.example.com. Required: a request with no Origin header is refused with 403. On a LIVE channel its host must equal the channel's registered domain (or one of its subdomains) or match an allowed origin. A TEST channel with no domain accepts any origin; once a domain is set it is matched as on LIVE.

X-SDK-Version?string

Version of the calling SDK (the Brainerce SDK sends it automatically). Informational: it is recorded on the channel and never changes the response.

The donor email. An address that is not a valid email still gets the 202.

email?string

Email address the donor gave with their monthly donation. Trimmed and cut to 320 characters.

Formatemail
Lengthlength <= 320
curl -X POST "https://api.brainerce.com/api/vc/string/donor-manage/request-link" \  -H "origin: string" \  -H "X-SDK-Version: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "accepted": true
}

{
  "statusCode": 403,
  "code": "INSUFFICIENT_SCOPE",
  "message": "API key does not have the required scope: products:write",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donor-manage/request-link"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donor-manage/request-link"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/vc/{connectionId}/donor-manage/request-link"
}