Complete REST API for FundlyHub — the AI-powered fundraising platform.
Authentication
The web app authenticates with httpOnly session cookies (access_token,
id_token, refresh_token) that POST /cognito/signin and the OAuth
callback set. Sign-in does not return tokens in its body.
Scripts and integrations authenticate with an API key: create one
with POST /api-keys from a signed-in session and send it as
Authorization: Bearer fh_live_…. A Cognito JWT is also accepted in the
same header. An API key acts as its owner, with every permission the
owner holds — treat it like a password. A key expires (at most one year
after it is created), cannot create or revoke API keys, and stops working
while its owner's account is not active. Send one credential per
request: either the session cookies or an Authorization header.
A missing credential answers 401, and so does a session token that is
present but invalid or expired (Invalid token, code: TOKEN_INVALID) or
that cannot be verified (code: TOKEN_VERIFICATION_FAILED): refresh the
session and retry. An API key that is unknown, expired or revoked, or whose
owner's account is not active, answers 401 (Invalid API key). 403 is
reserved for an authenticated caller who is not allowed to do something.
Operations that show no security requirement are genuinely public. Where
an operation lists both BearerAuth and {}, authentication is optional
and changes what comes back — an owner sees their own drafts, a signed-in
donor gets the gift attributed to their account.
Some endpoints additionally require a specific permission; where that
is the case the permission is named in the operation's description, and a
session without it gets 403 with required_permission in the body.
Several write endpoints also require a verified email address
(403 with code: EMAIL_NOT_VERIFIED) or sit behind a feature flag
(403 with feature_key). A flag with no stored setting counts as on.
Response envelopes
Most read endpoints wrap their payload: a single object comes back as
{ "data": { … } }, and a collection as { "data": [ … ] }, with
paginated collections adding { "pagination": { "limit", "offset", "total" } }. This is a convention rather than a rule — a handful of
endpoints return a bare object or a bare array, and each operation below
documents the shape it actually returns. Errors are always unwrapped:
{ "error": "…", "message": "…" }, sometimes with a machine-readable
code or a details array of validation failures.
Rate limits
A global limiter of 100 requests/minute per IP covers the API.
Public browsing reads — campaign, organization, category, platform-stats
and search reads, and the profile reads — are exempt from it, and most
of them count against a 300/min per IP bucket instead (an operation
that can answer 429 lists it). Other routes add a tighter bucket on
top of the global one:
- Authentication (sign-up, sign-in, password reset and change, unsubscribe writes, platform tips): 10/min per IP
- Verification resends and private-contact changes: 5/min per user (per IP for a signed-out resend)
- Public writes that send mail or store free text (receipt emails, donor notes, DMCA notices, ambassador applications): 5/min per IP
- Financial and endorsement endpoints (earnings, Stripe Connect, endorsements, receipt resends): 100/min per account
- Organization creation: 5/hour per (IP, user)
- Organization member lookups and adds (
GET /org-admin/{slug}/users/search,POST /org-admin/{slug}/members): 30/min per user, one bucket for both - Campaign media and outcome-report writes: 30 per 15 minutes per (IP, user)
- Referral clicks: 120/min per caller; card share images: 120/min per IP; campaign presence heartbeats: 4/min per IP and campaign
- AI endpoints: 10/min per user, applied inside the handler
- AI image generation: 10/hour and 30/day per user
Exceeding a limit returns 429 with RateLimit-Policy,
RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset and
Retry-After headers. The AI endpoints' in-handler limit answers a bare
429 without them.
Contact
License
ProprietaryServers
Authentication
User registration, login, and session management via AWS Cognito
Operations
Register a new user
Create a new account via AWS Cognito. The account is confirmed immediately and a verification link is mailed to the address; the email stays unverified until that link is followed (see GET /cognito/verify-email), and several write endpoints refuse an unverified account. Gated by the features.user_registration flag — when the flag is off the endpoint answers 403. Rate limited to 10 requests per minute per IP. When the risk engine flags the caller (repeated sign-ups from one IP), a reCAPTCHA token must be sent as captchaToken.
Request Body
Responses
Account created; verification email sent.
Confirm registration
Confirm a Cognito account with a verification code. Sign-up already confirms the account itself, so this is needed only when POST /cognito/signup answered userConfirmed: false. It does not mark the email verified — that is the link sent by sign-up.
Request Body
Responses
Account confirmed
Sign in
Authenticate with email and password. Tokens are not returned in the body: the session is set as httpOnly cookies (access_token, id_token, refresh_token) that the browser sends on every later request. Scripts and server-to-server integrations should use an API key instead (see Authentication). When the risk engine flags repeated failures, a reCAPTCHA token must be sent as captchaToken. If Cognito asks for a further challenge, the body carries challengeRequired: true, challengeName and session and no cookies are set.
Request Body
Responses
Authentication successful; session cookies set.
Refresh tokens
Exchange the refresh_token cookie for new access and ID token cookies. There is no request body — a refresh token sent in JSON is ignored.
Responses
Tokens refreshed; new access_token and id_token cookies set.
Sign out
Get current user
The signed-in user's profile. Cookie session only — this endpoint does not accept an Authorization header or an API key. When the short-lived session has expired but the refresh cookie is still valid, it renews the session transparently and sets new cookies. While FundlyHub support is viewing the account on the user's behalf it returns that user with is_impersonating: true and impersonated_by.
Responses
Current user info
Request password reset
Send a password reset code to the user's email. Answers 200 whether or not the address has an account, so it cannot be used to discover accounts.
Request Body
Responses
Reset code sent, if the account exists
Reset password
Initiate OAuth login
Redirect to the Cognito Hosted UI for a third-party provider. After the provider signs the user in, GET /cognito/oauth/callback sets the session cookies and redirects to redirect on the FundlyHub site.
Open this URL in the browser that will be signed in, by navigating to it rather than fetching it. The response sets a short-lived httpOnly cookie that the callback requires. A sign-in completed in a different browser, after about ten minutes, or after a newer sign-in was started in the same browser is refused, and the browser is sent to /auth?error=state_mismatch with no session set; starting again fixes it.
Parameters
Path Parameters
"google""apple"Query Parameters
The path on the FundlyHub site to send the browser to after sign-in, such as /campaigns/help-food-bank?tab=updates. It must start with a single /; a full URL on the FundlyHub site is reduced to its path. Any other value, such as a URL on another host or a protocol-relative //host, is replaced by /. Defaults to /.
"/campaigns/help-food-bank"2048Responses
Redirect to the OAuth provider. Sets the short-lived sign-in cookie that the callback checks.
Change password
Changes the password of an email/password account, given the current one.
Cookie session only. A request authenticated with an Authorization: Bearer header or an API key answers 401 Not authenticated. Google and Apple accounts have no password to change.
Rate limited at 10 requests/minute per IP (authentication bucket).
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Password changed
Resend the email-verification link
Sends a fresh verification link to the account registered under email (matched case-insensitively against the login address, not the private contact address — for that use POST /users/me/private-contact/resend).
Always answers 200 with the same message, whether or not the address has an account and even when sending fails, so it cannot be used to discover accounts.
No authentication. Rate limited at 5 requests/minute, keyed by user when a session is present and by IP otherwise.
Request Body
Responses
Accepted (sent if the address is known)
Verify an email address (link target)
The URL in verification emails. Consumes the token and always redirects to the frontend with the outcome in a query parameter; it never returns JSON.
?verification= is one of: success; invalid (no token); failed (unknown, used or expired token); contact_taken (the link proved a private contact address another account already holds); error (unexpected failure).
When the link proved the account's login address, the Cognito email_verified attribute is updated too, and any pending ambassador invitation for that address is settled.
No authentication. Rate limited at 300 requests/minute per IP.
Parameters
Query Parameters
Responses
Redirect to {FRONTEND_URL}/?verification=<outcome>
OAuth callback (Google / Apple)
The Cognito Hosted UI's redirect_uri after a Google or Apple sign-in, started at GET /cognito/oauth/{provider}. Not called by API clients directly.
Exchanges code for tokens, provisions or links the profile, sets the access_token, id_token and refresh_token httpOnly cookies, and redirects to the URL carried in state — which is the redirect query parameter given when the flow was started, or the frontend root.
Every failure is also a redirect, to {FRONTEND_URL}/auth?error=…: unverified_account_exists (a provider identity linked to a native account whose address was never verified), missing_code, token_exchange_failed, invalid_token, oauth_error, or the provider's own error with a message.
Parameters
Query Parameters
Authorization code from Cognito.
Where to send the browser after a successful sign-in.
Responses
Redirect — to state on success (with session cookies set), or to /auth?error=… on the frontend.
Fundraisers
Create and manage fundraising campaigns
Operations
List fundraisers
A paginated list of public campaigns, newest first. No authentication; the result is the same whoever asks. Only published campaigns (active or ended) with visibility: public are listed — drafts, campaigns awaiting review, paused, rejected, unlisted and private campaigns never appear here, whatever is asked for — and deleted campaigns are excluded. Each item is a campaign card (FundraiserCard); fetch the detail read for the full campaign. Card text is translated into the reader's language (lang, then the language cookie, then Accept-Language) where a translation exists.
Near a point. With near=<lat>,<lng> the list holds only the campaigns whose city is within radius_mi miles (default 20) of that point, nearest first and then most raised, and each item carries distance_mi. Every other filter still applies, and so do the public-only rules above. A campaign's point is its CITY's centroid, geocoded on the server from the free-text location; a campaign whose location has no city-level point yet is not in a near list. For the cities to offer a picker, read GET /fundraisers/cities.
Only the parameters below are accepted. Any other query parameter is ignored.
Parameters
Query Parameters
active — live campaigns whose end date has not passed; closed — ended campaigns plus live ones past their end date; ended — the same as closed; all — both (the default). Any other value answers 400 with { "error": "Invalid status", "allowed": [...] }.
"active""closed""ended""all""all"Filter by category — its id, slug or name all match.
Free-text search over title, summary, story, category and location — the same match GET /search?scope=campaigns uses. Matches are ranked first.
true for projects only, false for fundraisers only; omit for both.
raised sorts by most raised first. Omit for newest first.
"raised"Language to translate card text into.
"en""ru""uk""es"Page size, 1–100. A larger value is treated as 100; a missing, zero, negative or non-numeric value as 20.
110020Number of campaigns to skip, 0 or more. A negative or non-numeric value is treated as 0.
00<lat>,<lng> in decimal degrees, e.g. 38.5816,-121.4944: latitude −90..90, longitude −180..180. Lists only the campaigns within radius_mi of the point, nearest first, then most raised (this order replaces sort and the search rank), each with distance_mi. Anything that is not two numbers in range — including an empty value or the parameter sent twice — answers 400 { "error": "invalid_near" }.
"38.5816,-121.4944""^\\s*[+-]?(\\d+(\\.\\d*)?|\\.\\d+)\\s*,\\s*[+-]?(\\d+(\\.\\d*)?|\\.\\d+)\\s*$"Radius in miles around near, 1–100, default 20. A larger value is treated as 100, a smaller one as 1, and a non-numeric one as 20. Ignored without near.
110020Responses
Paginated list of campaign cards
Create fundraiser
Create a new fundraising campaign.
Send status: "draft" to save a draft, which skips the publish gate. Any other status, including an omitted one, runs the publish gate (profile readiness, an image, reasonability and AI review), and a flagged campaign lands pending for review. Note that an omitted status that passes the gate is stored as draft, not active. Send status: "active" to publish.
Requires a bearer session and is gated by the features.fundraiser_creation flag. Publishing requires a verified email address. A caller whose email is not yet verified may still create a draft (status: "draft" sent explicitly) and may hold at most 5 drafts. Past that the answer is 403 with code UNVERIFIED_DRAFT_LIMIT. Any other status from an unverified caller answers 403 with code EMAIL_NOT_VERIFIED. A failing gate answers 403, not 401.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Fundraiser created
List cities with active fundraisers
Every city that has at least one active public campaign, for a "near" city picker: one row per city, with how many active public campaigns it has and a representative point to pass to GET /fundraisers?near=<lat>,<lng>. No authentication.
Active means what GET /fundraisers?status=active lists: status active, end date not passed, visibility: public, not deleted. Cities come from a server-side, city-level geocode of each campaign's free-text location; campaigns whose location has no city (not yet geocoded, not a place, or a whole state or country) are not counted. lat/lng is the average of that city's campaigns' points, which are all the city's centroid, to 2 decimals. Sorted by count, most first, then by label. label is "City, REGION" in the US and "City, Country" elsewhere. Cached for up to 5 minutes.
Responses
Cities with active public campaigns
Get fundraiser
Retrieve a single fundraiser by UUID — the stored row, without the joined owner and raised figures the slug read adds. Authentication is optional and widens what you can see: a campaign that is not active, paused or ended, is private, or has been deleted, is returned to its owner and answers 404 to everyone else. An unlisted campaign is readable with its link. The owner also gets trustStatus and the private fields they entered, such as beneficiary_contact; every other caller, signed in or not, gets the campaign without them.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Query Parameters
Overlay the stored translation for this language, when one exists.
"en""ru""uk""es"Responses
Fundraiser details
Delete fundraiser
Soft-deletes a fundraiser (sets deleted_at). Only the owner may delete. A campaign that has collected funds cannot be deleted and answers 400. Once deleted, GET /fundraisers/{id} and GET /fundraisers/slug/{slug} answer 404 to everyone but its owner.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Fundraiser soft-deleted
Update fundraiser
Update fundraiser details. Only the owner can update. Setting status is how a draft is published (active) or sent for review (pending). Publishing runs the same gate as creation — profile readiness, at least one image, reasonability and AI review — and a flagged campaign lands pending. A campaign that has collected money cannot go back to draft.
The caller's email must be verified, with one exception: an unverified caller may edit a campaign that is currently draft, as long as the body sends no status or sends status: "draft". Moving a draft to any other status, or editing a campaign that is not a draft, answers 403 with code EMAIL_NOT_VERIFIED.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Request Body
Responses
Fundraiser updated
Get fundraiser by slug
Retrieve a fundraiser using its URL-friendly slug, with the owner, category name, raised figures and share count joined in. Authentication is optional and widens what you can see, exactly as on GET /fundraisers/{id}: unpublished, private and deleted campaigns, the private fields such as beneficiary_contact, and trustStatus are for the owner only.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Query Parameters
Overlay the stored translation for this language, when one exists.
"en""ru""uk""es"Responses
Fundraiser details
Check slug availability
Check if a fundraiser slug is available. Every campaign counts, including drafts and soft-deleted ones.
Parameters
Path Parameters
Query Parameters
A campaign id to ignore — pass the campaign being edited so its own slug reads as available.
"uuid"Responses
Availability status
Get fundraiser statistics
Aggregate totals for one campaign. Readable exactly when GET /fundraisers/{id} is: a campaign that is not active, paused or ended, is private, or has been deleted answers 404 to everyone but its owner. Authentication is optional.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Campaign statistics
Get campaign stats (broken)
Not available: currently answers 500 for every campaign. Use GET /fundraisers/{id}/stats instead.
Intended as a public stat-tile read: raised and goal (cents), donor, update and view counts, dates and days left.
Parameters
Path Parameters
"uuid"Responses
Campaign stats (bare object; not currently reachable)
Run a pre-publish trust assessment
Runs the full trust assessment the platform applies at publish time, without publishing: profile and campaign blockers, suggestions, a trust score, an AI analysis of the story, and the moderation decision it would lead to. No request body. Campaign owner only.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Assessment (bare object)
Get a campaign's lifecycle timeline
Lifecycle events (created, submitted for review, approved, rejected, updated, update posted, goal reached, deleted), newest first, with who acted. Campaign owner only; works for soft-deleted campaigns too.
Actions taken by FundlyHub staff appear with role: "admin" and null id, name and email.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Timeline (unwrapped)
Report a campaign
Flags an active campaign for moderator review. One report per user per campaign: reporting again replaces the earlier reason and details. Requires a verified email address.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Request Body
Responses
Report recorded
Contact the organizer
Sends a message to the campaign's organizer by email. The organizer receives it with the sender's email as the reply address and answers by replying, so the organizer's own address is never revealed unless they reply. Authentication is optional: anyone who can read the campaign may write, with the same visibility rule as GET /fundraisers/{id} (a campaign hidden from the caller answers 404). Limited to five messages an hour per client address and, when signed in, per account. A message with more than three links is refused, and the name may contain none.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Request Body
Responses
The message was accepted for delivery.
Get the featured donor for a campaign
Feeds the campaign page's "{name} and N others have donated" row: one featured donor and up to three faces, chosen from the latest 100 paid, non-anonymous gifts. Authentication is optional and only changes WHO is featured: a signed-in viewer sees people they follow, or who follow them, first. Only public campaigns that are active, paused or ended return donors; anything else returns empty.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID; anything else answers 400.
"uuid"Responses
Featured donor (bare object)
Get related campaigns
Four lists of other people's live, public campaigns for the discovery rail at the bottom of a campaign page: same category (similar), same US state (nearby), 70–99% funded (almost) and newest (recent). An empty list means that tab is not shown. Public.
Titles and summaries are in the reader's language (?lang=, then the locale cookie, then Accept-Language) when a translation exists; when a language is resolved, each card also carries translation_source and, where the text was replaced, original_title / original_summary.
Parameters
Path Parameters
The campaign's UUID; anything else answers 400.
"uuid"Query Parameters
Cards per list, clamped to 1–12.
11212"en""ru""uk""es"Responses
The four lists (bare object)
Get a campaign's outcome report
The creator's published account of what the money did. Returns { "report": null } (not 404) when there is no report, when it is still a draft, and when FundlyHub has hidden it; the three cases are deliberately indistinguishable. Authentication is accepted and does not change the report; owners read their draft from /mine. The campaign itself is readable exactly when GET /fundraisers/{id} is: a campaign that is not active, paused or ended, is private, or has been deleted answers 404 to everyone but its owner.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
The published report, or null
Save or publish the outcome report
Creates or replaces the campaign's single outcome report, and with publish: true publishes it in the same call. Publishing is one-way: a published report stays published on later saves, and publishedAt keeps its first value. Only the campaign's owner may write it. The body is sanitised as rich text; the title is plain text. Shares the media-upload rate limit (30 per 15 minutes). Audit-logged.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Request Body
Responses
The saved report
Get my campaign's outcome report, draft included
The owner's view of the outcome report: a draft is returned, and a hidden report is returned with hiddenAt set. { "report": null } when none has been written. Campaign owner only.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
The report, or null
Get aggregate campaign stats
Counters for the /causes stat tiles, over public, non-deleted campaigns in active or ended status, filtered the same way as the listing. "Closed" means ended, or active with a past end_date. Every figure, total_donors and topCategories included, covers the same filtered set.
Counts and sums are returned as numeric strings.
No authentication.
Parameters
Query Parameters
"true""false""1""0"Category slug, id or name.
Responses
The aggregates (bare object)
Create donation
Record a pending donation row against a fundraiser. The Stripe fee is computed server-side and the donor is the session's account.
Requires a bearer session and a verified email address, is gated by the features.donations flag, and is protected by reCAPTCHA v3 (action donation) — send the token as recaptcha_token in the body or in the x-recaptcha-token header. Because this route already requires a verified email, a Cognito session may omit the token; an API key or impersonation session without one answers 400. A token that is sent is always verified: below the score threshold answers 403.
This is the bookkeeping half of a gift. Money is moved by POST /payments/create-intent + POST /payments/confirm.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Header Parameters
reCAPTCHA v3 token, action donation. Alternative to recaptcha_token in the body.
iOS only. The App Attest key id (standard base64, as DCAppAttestService.generateKey returns it) of a key registered with POST /app-attest/attest. Send with x-app-attest-assertion.
iOS only. base64 of the assertion from generateAssertion(keyId, clientDataHash: SHA256(raw request body)). The body must carry a fresh app_attest_challenge. A valid assertion replaces the reCAPTCHA token; the headers alone never do. An invalid one answers 403 with code APP_ATTEST_INVALID, APP_ATTEST_KEY_UNKNOWN (attest a new key) or APP_ATTEST_CHALLENGE_INVALID (fetch a new challenge), unless the request passes reCAPTCHA some other way.
Request Body
Responses
Donation created
List donations for fundraiser
Public donor wall for one fundraiser: paid donations only, newest first, with a true total count. Donor email and payment identifiers are never returned, and anonymous gifts have their donor name and avatar stripped. Amounts are net (donation minus the Stripe fee) so the list agrees with the creator's balance.
Only a campaign anyone may open by link (live, ended or paused; not private; not deleted) has a public donor wall. For any other campaign the answer is an empty data array with total: 0, the same as for an unknown id, unless the caller is signed in as the campaign's owner or an admin.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Query Parameters
Page size, clamped to 1–200. A missing, zero, negative or non-numeric value means 20.
120020A missing, negative or non-numeric value means 0.
00Responses
Paginated list of donations
Get donation by receipt ID
The donor's receipt. Public — the receipt id is the Stripe PaymentIntent (pi_…) or invoice (in_…) id, a capability token only the donor holds, so treat it as a secret: the response includes the donor's email and card details. Answers for a donation in any payment state, including pending.
Parameters
Path Parameters
The receipt id, or the donation's PaymentIntent id.
Responses
Receipt details
Send donation receipt
Email the receipt for one donation.
The donation is named by receipt_id and nothing else — every figure
in the email is read from that row, so the caller cannot dictate the
contents. receipt_id is the Stripe PaymentIntent id, which is not
guessable.
recipient_email is deliberately free-form, because "send it to my
accountant" is the feature. What bounds it instead is a cap of 5
sends per donation, after which this returns 429 for that donation
permanently.
Authentication is optional; the endpoint is rate limited.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Receipt sent.
Get a campaign's top donors
The campaign's biggest donors, one row per person across all their paid gifts. Anonymous donors keep their total but not their name; they are identified by an opaque anonymous_key. When any donor gave in more than one currency, meta.mixed_currency is true and the totals should not be shown. Public.
Only a campaign anyone may open by link (live, ended or paused; not private; not deleted) has a public ranking. For any other campaign the answer is an empty data array, the same as for an unknown id, unless the caller is signed in as the campaign's owner or an admin.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Query Parameters
Rows to return, clamped to 1–25.
1255Responses
Top donors
List recent gifts (public feed)
Paid gifts for the homepage hero, newest first, in one of two modes:
- Unscoped (no
slugs): the newestlimitgifts platform-wide. - Per campaign (
slugs): the newestperCampaigngifts for
each named campaign, ranked within that campaign.
Only gifts to public campaigns in active or ended status appear, and gifts a moderator pulled from the feed never do. An anonymous gift has its name, avatar, city and ordinal nulled. amount_cents is the net amount (after the Stripe fee) — the same figure the campaign page shows. Campaign titles are localised as for GET /users/{id}/campaigns.
Publicly cached for 15 seconds. No authentication. Rate limited at 300 requests/minute per IP.
Parameters
Query Parameters
Unscoped mode only.
12412Comma-separated campaign slugs (or a repeated parameter). At most 6 are used; extras are ignored.
Per-campaign mode only.
1403"en""ru""uk""es"Responses
The gifts
Get the donor's note
The note already on a paid donation, so the composer can show it and its remaining writes (3 − note_edit_count). data is null when there is no note.
No authentication: the receipt id is the capability. Rate limited at 300 requests/minute per IP.
Parameters
Path Parameters
The receipt id, or the donation's Stripe PaymentIntent id.
Responses
The note, or null
Post, edit or retract the donor's note
Writes the note a donor leaves on their receipt, which appears in the campaign's comments. The receipt id is the authorisation — guest donors have no session — and the donation must be paid and to a campaign. A missing, unpaid or person-targeted donation gets the same 404.
Each donation allows 3 writes in total (post, edits and retract combined); after that every write is 429. Read the current note with the GET first so a reload does not spend one. The note is attributed to an account only when the caller is signed in as the donor.
Authentication is optional. Behind features.comments. Rate limited at 5 requests/minute per IP.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The receipt id, or the donation's Stripe PaymentIntent id.
Request Body
Responses
Note retracted
List categories
Get all category statistics
Organization count, active-campaign count and funds raised for every active category, in display_order. Only public campaigns and approved or verified organizations are counted.
Responses
Statistics for all categories
Get category
Get category statistics
Organization count, active-campaign count and funds raised for one category. Only public campaigns with status active and approved or verified organizations are counted.
Parameters
Path Parameters
Category id (an integer) or slug.
Responses
Category statistics with fundraiser counts
List organizations
Public list of approved/verified, non-deleted organizations. Returns their public fields plus a true total count. Rate-limited.
Parameters
Query Parameters
Only approved or verified take effect; other values fall back to the default public filter.
"approved""verified"Clamped to 1..100. Defaults to 20.
11002000Responses
Paginated list of organizations
Create organization
Multi-tier onboarding payload.
Requires a bearer session and a verified email address — an unverified session answers 403. Self-service creation is capped at 5 organizations per hour per (IP, user).
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Organization created, pending verification. The caller becomes its org_owner.
Get the caller's organizations
Organizations the authenticated caller holds an active org-scoped role on. Flat array, no pagination.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
Caller's organizations
Get organization by ID or slug
Public read of a single org by UUID or slug. Anonymous callers only see approved/verified non-deleted orgs; an active member of the organization can also read it in its other states and additionally receives suspension/rejection metadata (suspension_reason, rejected_reason, suspended_until) and the unredacted contact_email. Authentication is therefore optional but not inert.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Organization UUID or slug.
Responses
Organization detail
Get organization stats
Campaign counts, funds raised, unique donors and followers for an org (UUID or slug). A bare object — not wrapped in data.
Parameters
Path Parameters
Responses
Organization stats
Get organization public team
Public "Our team" list for an org (UUID or slug). Filtered to active, publicly-visible, non-expired memberships whose user profile is not private.
Parameters
Path Parameters
Responses
Organization team members
Get organization updates feed
Public, latest-first updates feed for an org (UUID or slug). Offset pagination, limit clamped 1..50.
Parameters
Path Parameters
Query Parameters
1502000Responses
Organization updates
List public organization documents
Approved trust documents an organization admin has marked public, for an approved or verified org (UUID or slug). Metadata only — there is no public download.
Parameters
Path Parameters
Responses
Public documents
List organization role titles
Public reference list of non-deprecated position titles (the LinkedIn-style "position" picker on the org-admin Members page), ordered by sort_order then label. These titles are display-only and carry no permissions — they are distinct from the RBAC roles org_owner / org_admin / org_viewer. Pass an id from this list as org_role_title_id when adding a member.
Responses
Role titles
List campaign locations by US state
US states that currently have active campaigns, with a campaign count per state, sorted by state name. The state is parsed out of each campaign's free-text location (a two-letter code or full state name); campaigns whose location cannot be matched to a state are left out. Cached for 5 minutes. Drives the location filter on campaign browse.
Responses
States with active campaigns
Report an organization
Flags an organization for review by FundlyHub's trust team. A signed-in caller with a verified email address can report; no relationship to the organization is needed. Each user holds at most one report per organization — reporting again overwrites the earlier category and details and puts the report back to pending.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Organization UUID (a slug is not accepted and answers 404).
"uuid"Request Body
Responses
Report recorded
Users
User profiles and preferences
Operations
Get user profile
Public profile by UUID or profile_slug. Authentication is optional: the caller's own profile may include their email, and a profile marked private is redacted for every other viewer.
WHAT SURVIVES THE REDACTION (#1696): id, name, avatar, profile_slug, created_at and the four counters — campaign_count, total_funds_raised, follower_count and following_count — with their REAL values. The leaderboard has always published a private person's rank and impact, so a profile reporting zeroes beside a board reporting real figures would be two surfaces disagreeing about one person rather than privacy.
Dropped: bio, location, website, social_links, email, phone, private_contact_email, display_name, account_status, kyc_verified_at and account_kind. The last three are more than a name — a Team chip and a verification tick say something about the person, which is exactly what a private profile withholds.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
User UUID or profile_slug.
Responses
User profile — a bare object, not wrapped in data. The fields marked own profile only are present only when the caller is the profile's owner.
Update user profile
Update your own profile. The fields below are the whole writable set;
anything else in the body is ignored rather than rejected.
location has been accepted since #1654 and was missing from this
schema; profile_visibility is new in #1696.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Request Body
Responses
Profile updated
Get a user's organization memberships
Public org memberships for a user (UUID or profile_slug). Memberships marked not publicly visible, memberships of deleted organizations, and every membership of a private profile — even to its owner — are left out. 404 only when the user does not exist.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Responses
User organizations
Get a user's public activity feed
The public activity feed on a profile (#1693): six kinds of public act, ordered newest first. Accepts a UUID or a profile_slug; auth-optional.
campaign_launched (the campaign passed approval), achievement_earned (a badge, read through the same public projection the profile badge strip uses), commented, donated, update_posted and endorsed (declared, or their referral link for that campaign recorded a visit — the definition the Endorsed-by dialog and the profile chip already use).
Each row inherits the visibility of the thing it reports: a refunded or charged-back gift, a hidden or retracted comment, a withdrawn update, a soft-deleted campaign and a revoked award are all simply absent. Anonymous gifts never appear, for anyone, including the profile's owner. A campaign that is unlisted, private, paused, draft or deleted is never named. A profile with show_donations_on_profile off keeps every row type except donated.
All money is INTEGER CENTS, never dollars. amount_cents on a donated row is what the giver paid — the donation plus the platform tip — which is the same figure the profile's Impact stat sums, so the two cannot disagree.
Paging is a KEYSET cursor, not an offset: pass the nextCursor from the previous page back as cursor. A cursor this endpoint did not issue is a 400. nextCursor is null on the last page.
Parameters
Path Parameters
A user UUID or a profile_slug.
Query Parameters
Filter chip. giving is donated; fundraising is campaign_launched and update_posted; community is commented, achievement_earned and endorsed. An unrecognised value falls back to all. The summary block is NOT filtered — it is the strip above the chips and always counts every type.
"all""giving""fundraising""community""all"15020The opaque nextCursor from the previous page. Omit for page 1.
Responses
A page of the feed. A private profile answers 200 with an empty items array to everybody but its owner, rather than 404 — the page renders and the section is simply empty.
Get a user's public donation activity
"Causes I support" on a public profile — the aggregate plus one entry per supported campaign. Accepts a UUID or a profile_slug; 404 only when the user does not exist.
Anonymous gifts are excluded, always. A profile that is private, or that has show_donations_on_profile off, answers zeroes. Only settled (paid) gifts count, and only to a campaign a reader could actually open — a deleted, draft, private, paused or unlisted campaign is neither listed nor counted (#1694).
Each entry in recent_supported_causes is a full campaign card, the same read model /fundraisers serves the /causes grid from, so the section renders the card the rest of the product uses. total_cents figures are INTEGER CENTS.
total_donated_cents is the PUBLIC figure and excludes anonymous gifts. It is the GIVER-FACING value of those gifts — the donation plus the platform tip, which is what the card was charged — so it agrees with the profile activity feed's per-gift figures and with the leaderboard's given figure. /donor/me/summary counts every gift the owner made and is a different number by design; do not compare them.
currency is the ISO 4217 code total_donated_cents is denominated in, taken from the donor's most recent gift that named one. Read it: minor units are not always hundredths, so formatting the total without it is a 100x error for a zero-decimal currency, not just the wrong symbol.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Query Parameters
Causes per page. The profile rail asks for 6; the dedicated page asks for more.
148600Responses
User donation activity
Get a user's Impact and leaderboard rank
The figures behind the Impact stat on a public profile, read from the same all-time field /leaderboard is served from, so the two surfaces always agree. Accepts a UUID or a profile_slug. All money is integer cents. A user who has not moved money answers zeroes and a null rank rather than 404 — their referral impressions are still reported. A private profile answers its headline impact figure only, with the rest of the breakdown zeroed and rank null, to everybody but its owner. If the leaderboard field cannot be read the endpoint answers 200 with zeroes rather than failing the profile.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Responses
The user's Impact breakdown
Get a user's permissions
Role assignments and effective permissions for the user named in the path. Requires a bearer session. Callers may read their own; reading another user's requires the view_all_users permission, and any other caller gets 403. Answers can be several minutes old; for the caller's own, current permissions use GET /me/capabilities.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
User permissions — a bare object.
Get user preferences
Returns the caller's own preferences. Callers may only access their own — any other path id answers 403.
Accounts with no stored row get a default object rather than a 404, so the returned key set differs slightly between the two cases. suppressed_scopes is always present: it lists the notification scopes this account's email address has been unsubscribed from via an emailed link. Those suppressions are keyed by address, not by account, so they stop mail even while the matching toggle reads true — which is exactly why they are reported separately rather than folded into the toggles.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Responses
User preferences
Update user preferences
Upserts the caller's own preferences and returns the stored row. Callers may only update their own — any other path id answers 403. suppressed_scopes is read-only and is not accepted here; an address unsubscribed by an emailed link can only be resubscribed through /unsubscribe/resubscribe.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Request Body
Responses
Updated preferences — the stored row.
Upload user avatar
Upload the caller's own avatar from a base64-encoded image — JPEG, PNG or WebP, at most 5 MB decoded. Callers may only update their own.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Request Body
Responses
Avatar uploaded
Delete user avatar
Delete the caller's own avatar. Callers may only delete their own.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Responses
Avatar deleted
Self-deactivate account
Deactivates the caller's own account and clears auth cookies. Callers may only deactivate their own.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Responses
Account deactivated
Get the pending account deletion
The caller's pending account deletion. awaiting_payout is true once the 30 days have passed and deletion is waiting for money owed to the caller to be paid out.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
A deletion is pending.
Request account deletion
Schedules deletion of the caller's account 30 days from now. In the same step every personal campaign of theirs that is active, paused or pending is closed (status ended), so it stops taking donations, and every session is signed out: the cookies of this one are cleared, every refresh token is revoked and every API key is revoked. An access token already issued keeps working until it expires (at most an hour).
The person can sign in again during the 30 days, which is how they reach Cancel; GET /cognito/me and the sign-in response then carry deletion_scheduled_for. They cannot publish or reopen a campaign meanwhile (publish blocker account_deletion_pending).
After the 30 days the account is deleted once nothing is owed to the person: money not yet paid out is paid out to their verified payout account first, and deletion waits for it. Donation and payout records are kept, anonymised; everything else is deleted, including the sign-in. Campaigns they created for an organization are the organization's: they keep running, and when the deletion completes they are handed to an owner of that organization, unchanged.
Idempotent: while a request is pending, calling again returns 200 with the same schedule and changes nothing. Requires a signed-in session; refused for an API key and while FundlyHub support is viewing the account.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
A deletion was already pending; its schedule, unchanged.
Cancel account deletion
Cancels the caller's pending account deletion. Allowed until the deletion has completed. Campaigns the request closed go back to the status they had, if they are still closed and not deleted; any other campaign is left as it is. Requires a signed-in session; refused for an API key and while FundlyHub support is viewing the account.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
Cancelled.
Set private contact email
Sets the caller's private contact email (visible only to the FundlyHub team). Validates format, blocks disposable domains, and triggers a verification email when the address changes — unless it is the caller's own verified sign-in address, which counts as verified at once. Shares the verification-resend limit: 5 requests per minute per user.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Private contact email saved
Set phone number
Sets the caller's phone number (stored only; no SMS verification).
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Phone number saved
Get publish-readiness checklist
Returns the publish-gate checklist for the caller (hard blockers + soft suggestions). When the checklist is unavailable the answer is ready: true, checklist: null and migration_pending: true.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
Publish-readiness status
Check profile slug availability
Get a profile's trust badges
The trust badges (e.g. identity verified, brand ambassador) on a profile, newest first. id is a UUID or a profile slug.
A private profile answers { "badges": [] } to everyone but its owner — the same answer as a profile with no badges, so the response does not confirm that the profile is private.
Authentication is optional; it only matters for the owner.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Profile UUID or profile slug.
Responses
The badges (bare object, not the data envelope)
List a profile's campaigns
The campaigns shown on a profile: those the person runs, and those they have endorsed (is_endorsed: true), newest activity first. id is a UUID or a profile slug.
A visitor sees public campaigns in active or ended status. The profile's owner, identified by the session (there is no query flag for it), additionally sees their own draft, pending and paused campaigns and their unlisted and private ones. A private profile returns { "data": [] } to everyone but its owner.
Titles and summaries are returned in the reader's language when a translation exists (?lang=, then the language cookie, then Accept-Language).
Rate limited at 300 requests/minute per IP.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Profile UUID or profile slug.
Query Parameters
110060"en""ru""uk""es"Responses
The profile's campaign cards
Get a profile's share image
The 1200×630 PNG a shared profile link unfurls into: name, avatar, account kind, location, join date and campaign count. Intended for social crawlers. id is a UUID or a profile slug. The name is the profile's display_name: the name, else @handle, else FundlyHub member, never an email address.
A private profile, like a missing one, is a 404. Cached for five minutes, server-side and via Cache-Control.
No authentication. Rate limited at 300 requests/minute per IP.
Parameters
Path Parameters
Profile UUID or profile slug.
Responses
The image
Resend the private-contact verification email
Sends a new verification link to the caller's private contact email (set with PUT /users/me/private-contact). Links last 24 hours; a verified private contact email is required before publishing a campaign. If the address is already verified, nothing is sent and the response says so.
Rate limited at 5 requests/minute per user, a bucket shared with PUT /users/me/private-contact.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
Sent, or already verified
List top creators
Public profiles ranked by money raised, then followers. Cached server- side; cached says whether this answer came from the cache.
fundsRaised is in DOLLARS (a decimal), unlike the rest of the API, which uses integer cents.
No authentication.
Parameters
Query Parameters
110020Responses
The creators (bare object with data, not paginated)
Platform
Platform stats and health
Operations
Public leaderboard
One ranked list on impact = raised + given + driven, in integer cents, over the chosen period.
Raised is the sum of the person's counted campaign cards — settled gifts to public fundraisers they organize, net of processor fees, their own gifts included;
Given is every settled gift they made under their name; Driven is other people's settled gifts through
their share or ambassador link to fundraisers they do not organize. Every dollar counts once.
Anonymous donors appear as alias rows keyed on an opaque anonymousKey; guest and anonymous rows link to their donor page under /d/{kind}/{key}.
When the request carries a session, standing describes where the caller ranks on the full view.
q searches the ranked board by name without renumbering it: a match keeps the rank it has on the full view.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
"all""30d""7d""all"Everyone; people organizing a public fundraiser; ambassador role holders; or donors — everyone with a settled gift under their name, whatever their role, anonymous identities included. Everyone, creators and ambassadors rank on impact; donors rank on given (see metric). A filtered view is ranked within itself.
"all""creators""ambassadors""donors""all"00110025Name search over this view. Trimmed and cut to 100 characters.
100Language for badge titles and anonymous aliases (en, ru, uk, es). Defaults from Accept-Language.
Responses
One page of the ranked view
Public donor page
The public page behind a guest or anonymous donor row on the leaderboard.
A donor identity is addressed ONLY by an opaque public key of 20 hex characters. The key is
one-way: no email, no identity id and no account reference ever leaves the server, and the
guest key and the anonymous key of one person cannot be related to each other.
given and gifts cover every settled gift of the identity — the same figures the
leaderboard row shows — including gifts to campaigns that are not public. The campaigns
list is public campaigns only, and campaignsHidden counts the gifts the list cannot show.
An anonymous identity is never named: name is null and anonymousKey carries the key, from
which the client derives the same localized alias the donor wall shows.
No authentication: the page is viewer-independent. Money is in integer cents.
Parameters
Path Parameters
"guest""anon"The opaque public key from a leaderboard row or a search result.
"^[0-9a-f]{20}$"Responses
The donor page
Get platform statistics
Public endpoint returning aggregate platform stats. A bare object, not wrapped in data.
Responses
Platform statistics
Get trust center status
Live security and compliance status for the Trust Center. Public; cached for up to 15 minutes.
Responses
Trust status
ZIP code lookup
Look up city and state from a 5-digit US ZIP code. Public; answers are cached.
Parameters
Path Parameters
"95630"Responses
Location data
Get feature flags
The features.* flags, read-only, for the signed-in caller — so the client can hide what the server will refuse. Flags that only concern FundlyHub's own operations are omitted. Each value is reduced to the three gating fields.
The server treats a flag that has no row as enabled, so a flag absent from this list is not necessarily off.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
The flags
Get the platform's published numbers
The figures FundlyHub publishes about itself on /@fundlyhub. The same answer for every reader, cached for five minutes; computed_at says how old it is. GET /stats is unchanged and still served.
No authentication. Rate limited at 300 requests/minute per IP.
Responses
The numbers (bare object)
List the FundlyHub team
The FundlyHub team members shown on /@fundlyhub: staff with a public, slugged profile and an account in good standing — at most 48, ordered by role then name. An empty list is a normal 200.
No authentication. Rate limited at 300 requests/minute per IP.
Parameters
Query Parameters
Cache key for future localised fields. Anything else is treated as en.
"en""ru""uk""es""en"Responses
The roster
List platform ambassadors with impact
Every holder of the ambassador role who is not banned (at most 100), ranked by impact. total counts all of them; members lists only those with a public, slugged profile, so total can exceed members.length.
No authentication. Rate limited at 300 requests/minute per IP.
Parameters
Query Parameters
"en""ru""uk""es""en"Responses
The ambassador rail
Tip FundlyHub
Starts a tip to FundlyHub itself — not a donation to a cause, and not tax-deductible. Creates a pending tip row and a Stripe Checkout Session (payment mode for one_time, monthly subscription mode for recurring) and returns its url; send the browser there. Stripe returns the payer to /tip/{tip_id} on the site. The tip is settled by the Stripe webhook, not by this call.
Authentication is optional. With a session the tip is attributed to the account, and the account's name and email are used when the body does not give them. Currency is always USD.
Rate limited at 10 requests/minute per IP (authentication bucket).
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Checkout started
Get a tip receipt
The public receipt for one tip: amount, cadence, status and dates, and the signed-in tipper's profile name unless they tipped anonymously. Never an email or a Stripe identifier.
A tip that has not settled yet is a 200 with status: pending — the payer often arrives from Stripe before the webhook does. A monthly tip also carries subscription; its next charge date is read from Stripe once the tip is paid.
No authentication: the tip id is the capability. Rate limited at 300 requests/minute per IP.
Parameters
Path Parameters
"uuid"Responses
The receipt (bare object)
Email a tip receipt
Emails the receipt for one paid tip to any address — every figure comes from the tip row, so the caller controls only the recipient. Capped at 5 sends per tip, after which this answers 429 for that tip permanently. A missing and an unpaid tip get the same 404.
The mail queue is processed immediately; message says whether the email was sent or only queued.
Authentication is optional. Rate limited at 5 requests/minute per IP.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Request Body
Responses
Sent or queued
Get the homepage live-activity feed
The homepage hero's live chips, newest first: gifts, referral-link views and clicks, people viewing a campaign right now, campaigns submitted for review (never named), and achievements earned. Every event passes the same public gates as the surface it comes from — only public, active or ended campaigns are named, and anonymous donors are not.
events is a discriminated union on kind. The same answer for every reader of a language; publicly cached for 15 seconds.
No authentication. Rate limited at 300 requests/minute per IP.
Parameters
Query Parameters
14024Campaign titles in this language when translated.
"en""ru""uk""es"Responses
The feed
Send a campaign-page presence heartbeat
Records that a visitor has a campaign page open; feeds the "N people viewing" chip in GET /home/activity. Designed for navigator.sendBeacon — no body. The visitor is the visitor_id cookie, or the per-tab v query parameter when there is no cookie; only a one-way hash of it is stored, and it expires on its own.
No authentication. Rate limited at 4 requests/minute per IP and campaign, then the 300/minute public bucket.
Parameters
Path Parameters
Fundraiser UUID.
"uuid"Query Parameters
Per-tab visitor id, used only when there is no visitor_id cookie.
"^[A-Za-z0-9_-]{8,64}$"Responses
Recorded (no body)
Get donor summary
Aggregate giving stats for the authenticated donor, anonymous gifts included. lifetimeAmountCents counts paid gifts only (no refunds or failures); the counts include every gift that left pending. A bare object.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
Donor summary
Get donor donation history
Paginated giving history for the authenticated donor with optional filters.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
11110020"paid""refunded""failed""pending""uuid"Responses
Donor donations, newest first
Download annual giving statement
Generates a year-end giving summary for the authenticated donor as PDF or CSV (binary download). Includes only paid donations in the requested calendar year. Not a tax receipt.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
Calendar year. Between 2026 and the current year.
2026"pdf""csv"Responses
Generated statement file
Full-text search
Search across campaigns, organizations, users and donors. Queries shorter than two characters return an empty result set rather than an error. Authentication is optional and does not change the result set. Donor rows cover guest donors (matched on the name given at checkout — never on an email) and anonymous identities (matched on the localized alias the donor wall shows for that identity), and they link to /d/{kind}/{key}.
Two limits apply to donor rows specifically. They are returned on the FIRST PAGE ONLY (offset=0); the donor ranking is a single highest-given-first list with no stable second page, so a later offset would repeat the same rows rather than continue them. And a guest-donor name match needs three consecutive alphanumeric characters somewhere in the query — An, a_n and ___ match no guest donor. Anonymous alias matching is unaffected and keeps the two-character minimum. Campaigns, organizations and users page normally and have no such floor.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
Search query. Fewer than 2 characters returns an empty result set.
Limit to a specific resource type.
"all""campaigns""users""orgs""donors""all"20100Pages campaigns. Donor rows are returned only at offset=0 — see the description above.
0Language an anonymous donor's alias is matched and cached in. Defaults from Accept-Language.
"en""ru""uk""es"Responses
Search results
Autocomplete suggestions
Typeahead suggestions drawn from the titles of active, public campaigns: a prefix of the original title or of any translated title matches, and the suggestion is the title in the reader's language (lang, the language cookie, then Accept-Language). Queries shorter than two characters return an empty list.
Parameters
Query Parameters
1020Responses
Suggestions
Payouts
Earnings and payout management
Operations
Get user earnings
Returns the authenticated creator's earnings summary in integer CENTS (#1499) — every field is suffixed _cents and must be divided by 100 before display. The target is always derived from the authenticated principal — any ?userId query param is ignored (#1072). total_cents and fees_cents are lifetime aggregates from the canonical ledger. For creators with an active Stripe Connect account, available_cents, pending_cents and withdrawn_cents come from Stripe directly (pending_cents additionally includes platform-held net funds that haven't transferred yet). Creators without a Connect account see available_cents and withdrawn_cents as 0 and pending_cents equal to total_cents (everything sits on FundlyHub's platform balance awaiting onboarding). Rate limited to 100 requests per minute per account.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
Earnings summary (all values are integer cents)
Get pending earnings breakdown
Per-donation attribution of the creator's not-yet-available money, lazy-loaded by the Earnings tab's Pending-tile dialog (#1131). The target is always the authenticated principal. Each donation is classified into one of three states: destination_charge_pending (Stripe holds it for this creator, card settlement pending), platform_held_unsettled (platform balance, awaiting settlement), or platform_held_settled (platform balance, eligible for release now). Rows already transferred to the creator's Stripe balance, settled destination charges, and rows under review are excluded. Rate limited to 100 requests per minute per account.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
Pending breakdown (all amounts in cents)
List Stripe connected accounts
Returns the authenticated user's Stripe Connect accounts. Refreshes each account's enabled/onboarding flags from the Stripe API on read (transient downgrades are suppressed unless Stripe reports a concrete requirement). Returns an empty array if the user has no connected account. Rate limited to 100 requests per minute per account.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
Connected accounts (array; empty if none)
Start or resume Stripe Connect onboarding
Creates the caller's Stripe Connect account if they do not have one, then returns a fresh onboarding link in either case. Send the creator to onboardingUrl; the link is single-use and short-lived, so call this again rather than caching it.
If the stored account no longer exists at Stripe, a new account is created transparently. Requires a bearer session; rate limited to 100 requests per minute per account.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Account id plus a fresh onboarding link
Get connected-account status
Reads one connected account straight from Stripe and refreshes the cached flags on FundlyHub's side. The account is addressed by its Stripe id, which you get from GET /stripe/accounts.
Ownership: accountId must be a connected account the caller owns: their own account, or an organization's account where the caller holds org.read_payouts in that organization. Any other id, including one that does not exist, returns 404; the response does not distinguish the two.
This is a pure read: it never moves money. Requires a bearer session; rate limited to 100 requests per minute per account.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Stripe connected-account id.
"acct_1AbCdEfGhIjKlMnO"Responses
Account status
Create an embedded-components account session
Mints a Stripe Account Session client secret for the embedded Connect components (account_onboarding, payouts, payments). Omit accountId to use the caller's most recent connected account.
Ownership: an explicit accountId must be a connected account the caller owns: their own account, or an organization's account where the caller holds org.manage_payouts in that organization. Any other id, including one that does not exist, returns 404 and no session is created.
Requires a bearer session; rate limited to 100 requests per minute per account.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Account session
List transfers to the creator's Stripe balance
Platform → creator transfers recorded for the authenticated caller, newest first. These are movements onto the creator's Stripe balance; payouts to their bank are a separate list (GET /stripe/connect/payouts). Requires a bearer session; rate limited to 100 requests per minute per account.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
101000Responses
Transfer rows (bare array)
List payouts to the creator's bank
Payouts from the creator's Stripe balance to their bank account. Read live from Stripe so the list matches what the creator sees in their bank; if Stripe is unreachable the endpoint falls back to FundlyHub's own records, which only cover payouts this API initiated or that a webhook recorded.
arrival_date and created are Unix timestamps in seconds, not ISO strings. Stripe paginates by cursor, so offset only applies to the database fallback. Requires a bearer session; rate limited to 100 requests per minute per IP.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
10100Only honoured by the database fallback path.
0Responses
Payout rows (bare array)
Withdraw available balance to the bank
Initiates a standard payout from the caller's connected Stripe balance to their bank account. Omit amountCents to withdraw the full available balance.
The minimum payout is $1.00 (100 cents) and the amount may not exceed the available balance. The caller must have a payout-enabled connected account. Gated by the features.payouts flag; requires a bearer session; rate limited to 100 requests per minute per account.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Payout initiated
Refresh payouts and withdrawals from Stripe
Re-reads every payout of the caller's connected Stripe account and repairs FundlyHub's own records of them: the payout rows, the link between each payout and the donations it carried, and the payout entries in the financial ledger. These are what a campaign's Updates feed (GET /projects/{fundraiserId}/updates) lists as withdrawals, so a payout Stripe made on its automatic schedule shows up there without waiting for a webhook. Moves no money. Never a dry run.
Omit accountId to refresh the caller's own account. An acct_… id of an organization account the caller can manage payouts for is also accepted; any other id answers 404, like an unknown one.
Requires a bearer session; rate limited to 100 requests per minute per account, and to one refresh per account per minute (429 with Retry-After).
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Refresh result
Get payout methods and schedule
The bank accounts and debit cards attached to the caller's connected account, plus the payout schedule Stripe currently applies. Requires a bearer session; rate limited to 100 requests per minute per account.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
Payout methods and schedule
Update the payout schedule
Sets how often Stripe pays the creator out. weekly requires weeklyAnchor (a weekday name), monthly requires monthlyAnchor (1–31); manual and daily take neither. Requires a bearer session; rate limited to 100 requests per minute per account.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
The schedule Stripe now applies
Get a Stripe dashboard link
Returns a URL into Stripe for the caller's connected account — a single-use Express login link deep-linked to payouts for Express accounts, or the Connect dashboard URL for other account types. Do not cache it. Requires a bearer session; rate limited to 100 requests per minute per account.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
Dashboard URL
List milestones
Get all milestones for a fundraiser, earliest due date first. Readable exactly when GET /fundraisers/{id} is: a campaign that is not active, paused or ended, is private, or has been deleted answers 404 to everyone but its owner. Authentication is optional.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Query Parameters
Overlay the stored translation of title and description for this language, when one exists.
"en""ru""uk""es"Responses
List of milestones
Create milestone
Add a milestone to a campaign. Only the campaign owner may post one, the caller's email must be verified, and the endpoint is gated by the features.milestones flag.
The request body is camelCase (targetAmountCents, dueDate) while the response row is snake_case (target_amount_cents, due_date). Both are in CENTS; target_amount_cents is also accepted in the body. The dollars-era targetAmount is not, and is ignored.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Request Body
Responses
Milestone created
Get milestone
A single milestone by id. Public when its campaign is: a milestone of a campaign that is not active, paused or ended, is private, or has been deleted answers 404 to everyone but the campaign's owner. Authentication is optional.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Query Parameters
Overlay the stored translation of title and description for this language, when one exists.
"en""ru""uk""es"Responses
Milestone details
Update milestone
Partial update of a milestone. Only the owner of the parent campaign may update it, and the caller's email must be verified. Send only the fields you are changing; an empty body answers 400.
The request body is camelCase (targetAmountCents, dueDate, completedAt) while the response row is snake_case. Setting completedAt is what marks a milestone complete.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Request Body
Responses
Milestone updated
Delete milestone
Deletes a milestone. Only the owner of the parent campaign may delete it.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Deleted
Save a hand-edited milestone translation
Creates or overwrites one milestone's translation in lang as source: "human". The milestone must belong to the campaign in the path. Campaign owner only. Audit-logged. lang may not be the campaign's source language.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid""uuid""en""ru""uk""es"Request Body
Responses
The saved row
Create payment intent
Create a Stripe PaymentIntent for a donation. This is the first half of
the donation flow; confirm it with POST /payments/confirm once Stripe
reports the payment succeeded.
Every field is snake_case, and every amount is in integer cents.
amount_cents is the donation itself; tip_amount_cents is the
optional tip to FundlyHub. The charge Stripe sees is
amount_cents + tip_amount_cents, and it must clear Stripe's $0.50
(50 cent) minimum.
amount_cents was called amount. The value was always cents — only
the name has changed, so that it says so.
fundraiser_id decides where the money goes. Omit it and the
PaymentIntent is created against the platform account with no campaign
attribution — the donation is not credited to any campaign and no
transfer to a creator is scheduled. It is optional in the sense that
the request succeeds without it, not in the sense that it is safe to
leave out.
The campaign must be active and not deleted; anything else is
rejected (404 if the id is unknown, 409 if the campaign exists but
cannot accept money).
Authentication is optional — a signed-in donor gets the donation linked
to their account, an anonymous one does not. reCAPTCHA is enforced
(see below), except that a signed-in session whose email is verified
may omit the token. While donations are switched off platform-wide the
request is refused with 403 and no PaymentIntent is created.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Header Parameters
reCAPTCHA v3 token, action payment. Required unless supplied as recaptcha_token in the body, or the caller is a signed-in session with a verified email. Missing (guest or unverified) → 400; failing the score threshold → 403, whoever sent it.
iOS only. The App Attest key id (standard base64, as DCAppAttestService.generateKey returns it) of a key registered with POST /app-attest/attest. Send with x-app-attest-assertion.
iOS only. base64 of the assertion from generateAssertion(keyId, clientDataHash: SHA256(raw request body)). The body must carry a fresh app_attest_challenge. A valid assertion replaces the reCAPTCHA token; the headers alone never do. An invalid one answers 403 with code APP_ATTEST_INVALID, APP_ATTEST_KEY_UNKNOWN (attest a new key) or APP_ATTEST_CHALLENGE_INVALID (fetch a new challenge), unless the request passes reCAPTCHA some other way.
Request Body
Responses
PaymentIntent created
Confirm payment
Confirm a PaymentIntent that Stripe has reported as succeeded, and
settle the donation record.
The field is payment_intent_id (snake_case) — the value returned as
payment_intent_id by POST /payments/create-intent.
Safe to retry: a PaymentIntent whose donation is already paid is
returned as-is rather than double-counted. Stripe webhooks reconcile the
same donation independently, so a client that never reaches this call
still ends up with a settled donation — calling it just makes the
receipt available immediately.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Payment confirmed; the settled donation is returned.
App Attest
Apple App Attest for the FundlyHub iOS app. A request carrying a valid assertion may skip reCAPTCHA on POST /payments/create-intent and POST /donations, guest or signed in. See the Native Clients guide for the byte-level protocol.
Issue an App Attest challenge
A random, single-use challenge for the iOS app, valid for
expires_in_seconds (300). Fetch one before attesting a key and one
before every donation request that carries an App Attest assertion.
Public (guests call it). Rate limited to 30 a minute per address.
Answers 503 with code: APP_ATTEST_DISABLED while App Attest is not
configured on the server.
Responses
A fresh challenge (Cache-Control: no-store).
Register an App Attest key
Register a device key with its attestation. The server verifies the
certificate chain to Apple's App Attestation Root CA, that the
attestation was made for challenge
(clientDataHash = SHA256(UTF-8 bytes of challenge)), the App ID, the
environment, a zero counter and that key_id is the key's hash, then
stores the public key. Attest once per key; attesting a key that is
already registered changes nothing.
Public (guests call it). Rate limited to 10 a minute per address.
Request Body
Responses
Key verified and registered.
Enhance text
Rewrite or generate one campaign field with an LLM. Requires a bearer session and is gated by the features.ai_text_enhancement flag. Throughput is capped per user (10 requests per minute by default).
context.field selects the system prompt and must be one of summary, story or milestone; any other value falls back to the summary prompt. text is capped at the server's maximum length.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Enhanced text
Campaign AI chat
Conversational assistant for the campaign-creation wizard. Requires a
bearer session; throughput is capped per user (10 requests per minute
by default).
The response is a Server-Sent Events stream (text/event-stream),
not a JSON document. Each event carries one JSON object:
{"type":"token","content":"…"}— one streamed text token{"type":"campaign_data","data":{…}}— campaign fields the model extracted{"type":"done"}— stream complete{"type":"error","message":"…"}— the stream failed mid-flight
Only the most recent 30 messages of messages are sent to the model.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Server-Sent Events stream of tokens and extracted campaign data.
Detect category
Classifies a campaign into one of the platform's categories. Requires a bearer session; throughput is capped per user (10 requests per minute by default).
Send any combination of title, summary and description — they are joined, truncated to 500 characters and classified. At least one must be present and the joined text must be 3 characters or longer.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Detected category
Generate a project update
Writes or rewrites a campaign update's text with an LLM. Requires a bearer session and is gated by the features.ai_update_generation flag. Throughput is capped per user inside the handler (10 requests per minute by default).
context.fundraiserTitle is required. Up to three context.previousUpdates are folded into the prompt for generate and improve.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Generated text
Import a campaign from a URL
Reads a campaign page on another platform and extracts its fields with an LLM, for the "import from link" step of the campaign builder. Requires a bearer session; throughput is capped per user inside the handler (10 requests per minute by default).
The response carries a one-time scrape_token, valid for 30 minutes; pass it on POST /fundraisers when creating the imported campaign. When the LLM is not configured, only the page title is returned, with a note and no token.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Extracted campaign fields
Get current user capabilities
Scope-aware permissions and roles for the authenticated user. Requires a bearer session and nothing else — this is the endpoint a client should use to decide what to show. The answer always includes the caller's global roles; with an organization or fundraiser scope it adds the roles held in that scope. A bare object, not wrapped in data.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
Defaults to global. scopeId is required for the other two.
"global""organization""fundraiser""global"The organization or fundraiser id. Required unless scopeType is global.
"uuid"Responses
User capabilities
Notifications
In-app notifications for the authenticated user. Which events also generate email is governed by the toggles on /users/{id}/preferences, not by anything under /notifications.
List notifications
Returns the authenticated user's notifications, newest first, together with the unread count. The count always excludes archived rows, even when archived=true is requested.
There is no offset pagination here — raise limit (max 100) to see further back.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
20100Pass "true" to return archived notifications instead of the active ones. Any other value returns the active ones.
"true""false"Responses
Notifications payload
Delete notifications
Permanently deletes the given notifications for the authenticated user.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Deletion result
Mark notifications as read
Marks the given notification ids as read for the authenticated user.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Update result
Mark all notifications as read
Marks all of the authenticated user's notifications as read.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
Update result
Mark one notification as read
Marks a single notification as read for the authenticated user.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Responses
Update result
Archive notifications
Archives the given notification ids for the authenticated user.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Archive result
List my campaign updates
Updates posted on campaigns the authenticated user is connected to, newest first, each with its read state, plus the unread count. Read state is kept on the server, so it is the same on every device.
A campaign is included when the user follows its holder — the person running a personal campaign, or the organization an org campaign belongs to — and the campaign is public and active or ended; or when the user has a paid gift to it and it is active, ended or paused and not private (an unlisted campaign a donor holds the link to is included). The user's own campaigns and own updates are never included, nor deleted campaigns or withdrawn updates.
An update posted before the user started following or first gave is returned with is_read: true and never counts as unread.
Paged by an opaque keyset cursor: pass next_cursor back as cursor until it is null.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
20150The next_cursor of the previous page. A cursor this API did not mint is a 400.
Overlay translations into this locale, as GET /projects/{fundraiserId}/updates does.
"en""ru""uk""es"Responses
One page of the feed
Mark all my campaign updates as read
Marks every unread update in the user's feed read. With until, only updates posted at or before that instant (inclusive to the millisecond), so clearing the list a client is showing does not also clear an update that arrived after it was fetched.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Update result and the new unread count
Mark one campaign update as read
Marks one update read for the authenticated user. Idempotent: an update already read answers updated: 0 and keeps its first read_at. Any existing, non-withdrawn update may be marked, whether or not it is in the user's feed, so a client can mark an update read wherever it was opened. There is no "mark unread", as for notifications.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Update result and the new unread count
Check follow status
Returns whether followerId currently follows followingId. A signed-in caller may ask about any pair — follow edges are public.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
Defaults to "user".
"user""organization""user"Responses
Follow status
Follow a user or organization
Creates a follow edge from the authenticated caller to following_id. The follower is always the authenticated user and cannot be supplied by the client. Gated by the features.user_follow_user flag.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Follow created
Unfollow a user or organization
Removes a follow edge. The path followerId must match the authenticated caller.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"user""organization"Responses
Follow removed
List a user's followers
Returns the users that follow the given user. Emails are never exposed.
A PRIVATE PROFILE ANSWERS AN EMPTY ARRAY to anyone but its owner (#1696). This list publishes other people's names, avatars, handles and counts, one row per follow, so a private profile's social graph is not enumerable through it. The COUNTS remain public — they ride on the profile payload, which carries real figures for a private profile. 200 with [], not 404: the person exists, their graph is closed.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Query Parameters
201000Responses
Follower list
List who a user is following
Returns the entities the given user follows. Emails are never exposed.
A PRIVATE PROFILE ANSWERS AN EMPTY ARRAY to anyone but its owner (#1696). This list publishes other people's names, avatars, handles and counts, one row per follow, so a private profile's social graph is not enumerable through it. The COUNTS remain public — they ride on the profile payload, which carries real figures for a private profile. 200 with [], not 404: the person exists, their graph is closed.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Query Parameters
201000Responses
Following list
Recalculate follower counts
Recounts the caller's followers and followings, stores follower_count / following_count on the profile, and returns them. id must be the caller's own profile UUID; any other profile answers 403. The counts are also refreshed whenever a follow is created or removed, so this is only a manual resync. Requires a bearer session.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
The recounted figures
List comments for a fundraiser
Returns paginated comments for a fundraiser, newest first. Hidden and retracted comments are left out. A comment written as a donor's note from their receipt carries its gift (donation_amount_cents, donation_currency); on an anonymous gift the author fields are null and is_anonymous is true. Every row carries gif, a GIF object or null; a GIF comment's content may be empty.
Authentication is optional. Every row carries like_count; for a signed-in caller liked_by_me says whether they like it, and for a guest it is always false.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Query Parameters
50110000Responses
Comment list
Create a comment
Creates a comment (or reply) on a fundraiser. The author is derived from the authenticated user. Content must be 2000 characters or less, and is required unless gif_id is set.
gif_id attaches a GIF (#1963): a GIPHY id from GET /gifs/trending or GET /gifs/search. The server looks it up on GIPHY and stores the GIF object it builds itself; clients never send URLs. Only g and pg GIFs are accepted. A request with gif_id also counts against the GIF rate limit (60 a minute per account).
Requires a verified email address, and is gated by the features.comments flag — either gate failing answers 403.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Request Body
Responses
Comment created — the stored row (including gif, a GIF object or null) plus author_name and author_avatar.
Trending GIFs for the comment GIF picker
GIPHY's trending GIFs, rated pg or lower, as GIF objects. Cached on the server for a few minutes. Page with offset = the previous answer's next_offset.
Authentication is optional. Rate limited at 60 requests a minute per account (per IP for a guest), on top of the public limit.
Answers 503 { "error": "gifs_unavailable" } while GIF comments are off (features.comment_gifs) or GIPHY is not configured. Clients should hide their GIF button when they get it.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
24150004999Responses
A page of GIFs
Search GIFs for the comment GIF picker
GIPHY search, rated pg or lower, as GIF objects. Cached on the server by q, offset, limit and lang. Same limits and 503 behaviour as GET /gifs/trending.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
The search term. Whitespace is collapsed and it is cut to 50 characters.
124150004999The search language. Anything else is treated as en.
"en""ru""uk""es""en"Responses
A page of GIFs
Delete a comment
Deletes a comment. Only the comment's author may delete it. A donor's note tied to a gift is retracted (hidden, kept for the record) rather than deleted.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Comment deleted
Like a comment
Likes a comment or a reply as the authenticated user. Idempotent: liking a comment you already like changes nothing and answers the same state. No verified email is required. Gated by the features.comments flag and the per-account rate limit.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Liked; the comment's count after the write
Unlike a comment
Removes the authenticated user's like from a comment or a reply. Idempotent: unliking a comment you do not like changes nothing.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Not liked; the comment's count after the write
Updates
Project updates, milestones, and funding stats
Operations
Get the project updates feed
Returns a unified feed of the fundraiser's updates and its withdrawals, newest first — a bare array. A withdrawal row is this campaign's share of a payout that carried its donations (never the payout's whole amount); paid, in-transit and pending payouts are listed. Withdrawn updates are left out. An unreadable feed answers 200 with an empty array rather than an error.
Readable exactly when GET /fundraisers/{id} is: a campaign that is not active, paused or ended, is private, or has been deleted answers 404 to everyone but its owner.
Authentication is optional. Every update item carries like_count; for a signed-in caller liked_by_me says whether they like it, and for a guest it is always false. Withdrawal items carry neither.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Query Parameters
Overlay update translations into this language, when they exist.
"en""ru""uk""es"Responses
Update feed (items are type "update" or "withdrawal")
Post a project update
Creates a project update on a fundraiser. Only the fundraiser owner may post, the caller's email must be verified, and the endpoint is gated by the features.project_updates flag. The body field is required (at most 20,000 characters; title at most 200). The update's language is detected and machine translations are queued.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Request Body
Responses
Update created — a bare object, not wrapped in data.
Delete a project update
Soft-deletes a project update. Only the fundraiser owner may delete it.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Responses
Update deleted
Edit a project update
Changes an update's title and/or body. Campaign owner only; the caller's email must be verified, and the endpoint is gated by the features.project_updates flag. Send at least one of title and body; title: null (or blank) removes the title.
When the words change, the update's language is detected again, its machine translations are dropped and re-queued, and hand-made translations are flagged as outdated. A withdrawn update answers 404. Audit-logged.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid""uuid"Request Body
Responses
The edited update (bare object)
Like a project update
Likes a campaign update as the authenticated user. Idempotent: liking an update you already like changes nothing and answers the same state. No verified email is required; the per-account rate limit applies.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid""uuid"Responses
Liked; the update's count after the write
Unlike a project update
Removes the authenticated user's like from a campaign update. Idempotent: unliking an update you do not like changes nothing.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid""uuid"Responses
Not liked; the update's count after the write
List project milestones
Returns all milestones for a fundraiser, ordered by due date then creation date — a bare array, unlike GET /fundraisers/{fundraiserId}/milestones, which wraps the same rows in milestones. Readable exactly when GET /fundraisers/{id} is: a campaign that is not active, paused or ended, is private, or has been deleted answers 404 to everyone but its owner. Authentication is optional.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Query Parameters
Overlay the stored translation of title and description for this language, when one exists.
"en""ru""uk""es"Responses
Milestone list
Get project funding stats
Returns allocation, disbursement, and milestone-count stats for a project-type fundraiser. Non-project fundraisers return zeroed stats. Not available yet for project fundraisers: the endpoint currently answers 500 for them. Readable exactly when GET /fundraisers/{id} is: a campaign that is not active, paused or ended, is private, or has been deleted answers 404 to everyone but its owner. Authentication is optional.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Responses
Project stats
List translations of a campaign's updates
Every live (not withdrawn) update of the campaign, newest first, with its original text and language and one row per other supported locale. Campaign owner only.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid"Responses
Updates with their translations
Get one update's translations
One update of the campaign: its original and its rows in the other three locales. Campaign owner only. A withdrawn update, or one belonging to another campaign, answers 404.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid""uuid"Responses
The update and its translations
Save a hand-edited update translation
Creates or overwrites an update's translation in lang as source: "human", so no automatic pass overwrites it. Campaign owner only. Audit-logged. Refused in the update's own language. When the update has a title, the translation must carry one too.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid""uuid""en""ru""uk""es"Request Body
Responses
The saved row
Regenerate an update translation
Re-runs machine translation of one update into lang and stores it as source: "machine". A hand-edited row is only replaced with force=1. Campaign owner only. Audit-logged.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid""uuid""en""ru""uk""es"Query Parameters
1 or true overwrites a hand-edited row.
"1""true"Responses
The regenerated row
Track a share event
Records a social-share event for a fundraiser or profile. Authentication is optional — the sharing user is recorded when a session is present, otherwise the row is anonymous.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Share tracked
Campaign link-preview image
A 1200×630 PNG Open Graph card for the campaign: title, cover, organizer, raised and goal amounts and donor count. Public; served to social crawlers.
Only a campaign anyone may open by link (live, ended or paused; not private; not deleted) has an image. Any other campaign is a 404, the same as an unknown slug, for every caller.
The language comes from ?lang=, else the locale cookie, else Accept-Language, else the original text. Without ?lang= the response varies on Accept-Language, Cookie. Cached for 30 seconds.
Parameters
Path Parameters
Query Parameters
Language to render in. Without it the locale cookie, then Accept-Language, decide.
"en""ru""uk""es"Responses
PNG image
Campaign square share poster
A 1080×1080 PNG for feed posts (Instagram and similar). Same data, language rules, caching and 404 for a campaign that is not readable by link as the link-preview image.
Parameters
Path Parameters
Query Parameters
Language to render in. Without it the locale cookie, then Accept-Language, decide.
"en""ru""uk""es"Responses
PNG image
Campaign story card
A 1080×1920 PNG for Instagram and Facebook Stories. Same data, language rules, caching and 404 for a campaign that is not readable by link as the link-preview image.
Parameters
Path Parameters
Query Parameters
Language to render in. Without it the locale cookie, then Accept-Language, decide.
"en""ru""uk""es"Responses
PNG image
Campaign share kit
The link-preview image, the square poster and the story card in one zip download (<slug>-link.png, <slug>-post.png, <slug>-story.png), as linked from the ambassador endorsement-request email. Same language rules, 30-second caching and 404 for a campaign that is not readable by link as the single images. All three must render, or the call answers 404.
Parameters
Path Parameters
Query Parameters
Language to render in. Without it the locale cookie, then Accept-Language, decide.
"en""ru""uk""es"Responses
Zip archive, sent as an attachment named <slug>-share-kit.zip
Get a campaign's share impact
What sharing has done for this campaign, all-time: human visits that arrived through a shared link, how many came through endorsers' links, and the paid, non-self-referred gifts traced back to a share. Public. A campaign nobody has shared returns zeros. Readable exactly when GET /fundraisers/{id} is: a campaign that is not active, paused or ended, is private, or has been deleted answers 404 to everyone but its owner. Authentication is optional.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID; anything else answers 400.
"uuid"Responses
Share impact (bare object)
Get a campaign's shares per channel
Human clicks through shared links, and the gifts they led to, per share channel, all-time. Every channel is present, with zeros when none. Public. Readable exactly when GET /fundraisers/{id} is: a campaign that is not active, paused or ended, is private, or has been deleted answers 404 to everyone but its owner. Authentication is optional.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID; anything else answers 400.
"uuid"Responses
Per-channel stats (bare object)
Organization Admin
The org-scoped admin panel at /org-admin/{slug}/*. Every operation needs a bearer session, resolves {slug} to an organization (an unknown or malformed slug answers 404 Organization not found, never 403), and then checks an org-scoped permission held through the caller's org_owner, org_admin or org_viewer role on that organization. A caller without the permission gets 403 with message: "Requires permission: <name>".
Role → permission map: org_viewer holds org.read, org.read_donations, org.read_payouts and org.read_donors. org_admin adds org.update_settings, org.manage_members, org.manage_dbas, org.manage_locations and org.upload_documents. org_owner adds org.manage_children and org.manage_payouts.
Operations
Get organization dashboard totals
Headline numbers for the org-admin dashboard. For a conglomerate the figures roll up the organization and all of its child organizations; for a company they cover the organization alone. totalRaisedCents sums every paid donation on those organizations' fundraisers, in integer cents. uniqueDonors counts distinct signed-in donors only — guest donations do not add to it.
Requires permission: org.read
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Responses
Dashboard totals
List the organization's fundraisers
Non-deleted fundraisers owned by this organization (not its children), newest first, with offset pagination. Every status is included — drafts, pending review, ended — unless status narrows it. Private contact fields are never returned.
Requires permission: org.read
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Query Parameters
Only fundraisers with this status. Send one of the listed values.
"draft""pending""rejected""active""paused""ended"Clamped to 1..100. Defaults to 20.
11002000Responses
Paginated fundraisers
List donations to the organization's fundraisers
Donations to this organization's non-deleted fundraisers, newest first, with offset pagination. Every payment status is returned unless status narrows it.
Donor privacy: donor_display_name is Anonymous donor when the gift was marked anonymous, the donor's profile name for a signed-in donor, and the checkout name for a guest. Donor email addresses are never returned.
Requires permission: org.read_donations
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Query Parameters
Only donations with this payment status. Send one of the listed values.
"paid""pending""failed""refunded"Only donations to this fundraiser. Must be a UUID.
"uuid"Clamped to 1..100. Defaults to 20.
11002000Responses
Paginated donations
List the organization's donors
Distinct donors who made a paid donation to any of this organization's non-deleted fundraisers, with their lifetime totals here, largest first, with offset pagination. All anonymous gifts are collapsed into a single Anonymous donor row (donor_key: anonymous) so they cannot be told apart by amount. Signed-in donors are keyed user:<profile id>; guest donors are keyed guest:<24 hex characters>. Treat donor_key as an opaque identifier: it is stable across pages and requests, but carries no contact details and cannot be turned back into one.
Requires permission: org.read_donors
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Query Parameters
Clamped to 1..100. Defaults to 20.
11002000Responses
Paginated donor roll-up
Look up an account by email before adding it as a member
Confirms which FundlyHub account uses an email address, for the Members page's "Add member" form. q must be a complete email address; it is matched case-insensitively against account login emails only, and at most one result comes back, with the address masked. Anything that is not a complete email address (a name, a fragment, a phone number) answers 200 with an empty list. This is not a directory search.
Available only to organizations that are approved or verified; any other organization gets 403 with code: ORG_NOT_APPROVED.
Rate limited to 30 requests per minute per user, in one bucket shared with POST /org-admin/{slug}/members.
Note the bare { "results": [ … ] } envelope rather than data.
Requires permission: org.manage_members
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Query Parameters
A complete email address (surrounding whitespace is trimmed). Matched case-insensitively against account login emails.
"sam@example.org""email"254Responses
The matching account, or an empty list when no account uses that email or q is not a complete email address.
List organization members
Everyone holding an active, unexpired org-scoped role on this organization, highest role first, then by name. Includes each member's email address, which is why this sits behind the management permission rather than org.read.
Requires permission: org.manage_members
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Responses
Members
Add a member
Adds an existing FundlyHub user, found by email (case-insensitive), to the organization with the given role. The membership is active immediately; no invitation is sent and the user does not have to accept. org_owner cannot be granted here — add the user first, then promote them with PATCH /org-admin/{slug}/members/{userId}.
An optional display title can be set with either org_role_title_id (an id from GET /org-role-titles) or custom_role_title, not both.
Available only to organizations that are approved or verified; any other organization gets 403 with code: ORG_NOT_APPROVED.
Rate limited to 30 requests per minute per user, in one bucket shared with GET /org-admin/{slug}/users/search.
Requires permission: org.manage_members
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Request Body
Responses
Member added
Remove a member
Deactivates every org-scoped role the user holds on this organization. Only an org_owner may remove an owner, and nobody may remove a member whose role is equal to or above their own (403). Removing yourself (leaving) is always allowed. The organization's last remaining owner cannot be removed (400) — promote another member to owner first.
Requires permission: org.manage_members
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"The member's profile id. A non-UUID answers 404 Member not found.
"uuid"Responses
Member removed
Change a member's role
Moves a member to a different org role. Setting the role they already hold is an idempotent no-op that still answers 200.
Roles rank org_owner > org_admin > org_viewer. Rules enforced, each a 403 when broken:
- only an
org_ownermay promote anyone toorg_owner, or change the role of another owner; - nobody may change the role of a member whose role is equal to or above their own (an
org_adminmanagesorg_viewers, not other admins); - nobody may grant a role above their own.
Lowering your own role (stepping down) is always allowed. The organization's last remaining owner cannot be demoted (400) — promote another member to owner first.
Requires permission: org.manage_members
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"The member's profile id. A non-UUID answers 404 Member not found.
"uuid"Request Body
Responses
Role updated (or already held)
Update organization settings
Partial update of the organization's profile. Only the fields below are writable; others are ignored. String fields accept a string or null and are trimmed. At least one writable field must be present.
logo and banner_image take a URL as-is; to upload an image use POST /org-admin/{slug}/avatar or /banner instead.
Requires permission: org.update_settings
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Request Body
Responses
Updated settings
Upload organization logo
Uploads a new logo as base64 JSON. The image is centre-cropped to 256×256, re-encoded as WebP, stored, and written to the organization's logo straight away — no separate settings save is needed. Any previous logo file is deleted. Maximum 5 MB decoded.
Note the bare response body (no data envelope).
Requires permission: org.update_settings
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Request Body
Responses
Logo uploaded
Remove organization logo
Deletes the stored logo files and clears the organization's logo.
Requires permission: org.update_settings
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Responses
Logo removed
Post an organization update
Publishes a post to the organization's public updates feed (GET /organizations/{id}/updates). The caller is recorded as the author.
Requires permission: org.update_settings
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Request Body
Responses
Update posted
Delete an organization update
Permanently deletes one of this organization's updates. A malformed updateId answers 500 rather than 404.
Requires permission: org.update_settings
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$""uuid"Responses
Update deleted
Edit an organization update
Partial edit of one of this organization's updates. Send at least one of title, body, cover_image.
Requires permission: org.update_settings
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$""uuid"Request Body
Responses
Update edited
Upload organization banner
Uploads the public-profile banner as base64 JSON. The image is centre-cropped to 1500×500 (3:1), re-encoded as WebP, stored, and written to the organization's banner_image straight away. Any previous banner file is deleted. Maximum 8 MB decoded.
Note the bare response body (no data envelope).
Requires permission: org.update_settings
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Request Body
Responses
Banner uploaded
Remove organization banner
Deletes the stored banner files and clears the organization's banner_image.
Requires permission: org.update_settings
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Responses
Banner removed
List DBAs
The organization's "doing business as" names, default first, then alphabetical.
Requires permission: org.read
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Responses
DBAs
Add a DBA
Adds a DBA name. With is_default: true it becomes the default and the previous default is cleared in the same transaction.
Requires permission: org.manage_dbas
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Request Body
Responses
DBA added
Delete a DBA
Permanently deletes a DBA. Deleting the default leaves the organization with no default DBA.
Requires permission: org.manage_dbas
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"A non-UUID answers 404 DBA not found.
"uuid"Responses
DBA deleted
Update a DBA
Renames a DBA and/or changes whether it is the default. Setting is_default: true clears the previous default in the same transaction.
Requires permission: org.manage_dbas
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"A non-UUID answers 404 DBA not found.
"uuid"Request Body
Responses
DBA updated
List office locations
The organization's physical locations, primary first, then oldest first. Includes locations not shown publicly.
Requires permission: org.read
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Responses
Locations
Add an office location
Adds a location. With is_primary: true it becomes the primary location and the previous primary is cleared in the same transaction. Locations are hidden from the public profile unless is_publicly_visible is true.
Requires permission: org.manage_locations
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Request Body
Responses
Location added
Delete an office location
Permanently deletes a location. Deleting the primary leaves the organization with no primary location.
Requires permission: org.manage_locations
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"A non-UUID answers 404 Location not found.
"uuid"Responses
Location deleted
Update an office location
Partial update of a location. address, when sent, replaces the whole address object. Setting is_primary: true clears the previous primary in the same transaction.
Requires permission: org.manage_locations
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"A non-UUID answers 404 Location not found.
"uuid"Request Body
Responses
Location updated
Start or resume Stripe Connect onboarding
Creates the organization's Stripe Express account (business type company) if it has none, or reuses the existing one, and returns a fresh single-use onboarding link. Safe to call repeatedly — each call returns a new link. If the stored account no longer exists on Stripe a new one is created in its place.
The organization must have verification_status: verified, and the features.org_level_stripe_connect flag must be on (503 otherwise).
Requires permission: org.manage_payouts (organization owners only)
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Responses
Onboarding link created
Get Stripe Connect status
Whether the organization can take charges and receive payouts. Reads the account live from Stripe and refreshes the stored flags. If Stripe cannot be reached, the last stored flags are returned instead and the Stripe-only fields (defaultCurrency, country, businessType, requirementsCurrentlyDue) are absent. With no account yet the body is { "data": { "connected": false } }.
Requires permission: org.read_payouts
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Responses
Connect status
List child organizations
Organizations whose parent is this one, newest first. Always empty for a company; only a conglomerate has children.
Requires permission: org.read
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Responses
Child organizations
Create a child organization
Creates a new company under this conglomerate. Validation is the same as POST /organizations, except that the new organization is always a company (kind is ignored once it passes validation) and sending parent_organization_id in the body is rejected — the parent is always the {slug} organization. The caller becomes org_owner of the child. The child starts with verification_status: pending; the first DBA becomes its default and the first location its primary.
Requires a verified email address.
Requires permission: org.manage_children (organization owners only)
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Request Body
Responses
Child organization created
List verification documents
The organization's verification documents in every state except superseded, newest first, including review outcome and whether each is shown on the public profile.
Financial documents — w9, voided_check and other — are listed only when the caller also holds org.upload_documents; otherwise they are left out of the list. ein_letter and 501c3_determination are listed with org.read alone.
Requires permission: org.read
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Responses
Documents
Upload a verification document
Uploads a document for platform review as base64 JSON. It is stored privately and enters the review queue as pending. Uploading a doc_type the organization already has pending or approved marks the older one superseded. Approval of both ein_letter and 501c3_determination is what verifies the organization.
Size limit: 10 MB decoded by default, but the JSON body itself is capped at 10 MB, so in practice a file must stay under about 7.5 MB to fit once base64-encoded — larger bodies get 413 before the handler runs.
Requires permission: org.upload_documents
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"Request Body
Responses
Document uploaded
Download a verification document
Streams the stored file with its original Content-Type and, when a filename was recorded, Content-Disposition: attachment. A document belonging to a different organization answers 404.
A w9, voided_check or other document can be downloaded only by a caller who also holds org.upload_documents; anyone else gets 404, as if it did not exist.
Requires permission: org.read
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"A non-UUID answers 404 Document not found.
"uuid"Responses
The file
Delete a pending verification document
Withdraws a document that is still pending. Approved and rejected documents are kept for the audit trail and cannot be deleted — they answer 404 like a missing document.
Requires permission: org.upload_documents
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"A non-UUID answers 404 Document not found.
"uuid"Responses
Document deleted
Show or hide a document on the public profile
Sets the document's public-visibility flag. The public profile (GET /organizations/{id}/documents/public) lists a document only when it is both flagged public and approved, so the flag can be set ahead of review.
Requires permission: org.upload_documents
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The organization's slug (not its UUID).
"local-food-bank-a1b2c3""^[a-z0-9][a-z0-9-]{0,254}$"A non-UUID answers 404 Document not found.
"uuid"Request Body
Responses
Visibility updated
Translations
The owner-side Translations tab: a campaign's, its milestones' and its updates' rows in the other supported locales (en, ru, uk, es), hand-edited or regenerated. The public read paths already serve the translated text; these endpoints are for authors.
Operations
List a campaign's translations
The Translations tab's data for one campaign: its source text and language, one row per other supported locale (fields null and source: null where nothing has been translated yet), the row in the campaign's own language if one exists (original_row), and every milestone with its rows.
Only the campaign's owner may read it.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid"Responses
Source text and per-locale translations
Save a hand-edited campaign translation
Creates or overwrites the campaign's translation in lang with a hand-edited version and marks it source: "human", so automatic re-translation leaves it alone. Campaign owner only. Audit-logged.
lang may not be the campaign's stored source language; that text is edited on the campaign itself.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid""en""ru""uk""es"Request Body
Responses
The saved row
Discard a hand-edited version in the campaign's own language
Deletes the hand-edited (source: "human") rows in the campaign's OWN language, both the campaign's row and its milestones' rows. Such a version exists when someone edited that language while the campaign was labelled with another one; readers of that language are served it instead of the original until it is discarded. Machine rows for fields written in another language are rebuilt in the background afterwards.
Only works for lang equal to the campaign's stored source language; a translation into another language is replaced with regenerate instead. Campaign owner only. The discarded text is written to the audit log.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid""en""ru""uk""es"Responses
What was discarded
Regenerate a campaign translation
Re-runs machine translation of the campaign into lang and stores it as source: "machine". The locale's milestone rows are refreshed the same way. Campaign owner only. Audit-logged.
A hand-edited row is not overwritten unless force=1 (or true) is passed; without it the call answers 409. It also answers 409 when the campaign text or the row changed while the translation was being generated.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid""en""ru""uk""es"Query Parameters
1 or true overwrites a hand-edited row.
"1""true"Responses
The regenerated row
Save a hand-edited milestone translation
Creates or overwrites one milestone's translation in lang as source: "human". The milestone must belong to the campaign in the path. Campaign owner only. Audit-logged. lang may not be the campaign's source language.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid""uuid""en""ru""uk""es"Request Body
Responses
The saved row
List translations of a campaign's updates
Every live (not withdrawn) update of the campaign, newest first, with its original text and language and one row per other supported locale. Campaign owner only.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid"Responses
Updates with their translations
Get one update's translations
One update of the campaign: its original and its rows in the other three locales. Campaign owner only. A withdrawn update, or one belonging to another campaign, answers 404.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid""uuid"Responses
The update and its translations
Save a hand-edited update translation
Creates or overwrites an update's translation in lang as source: "human", so no automatic pass overwrites it. Campaign owner only. Audit-logged. Refused in the update's own language. When the update has a title, the translation must carry one too.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid""uuid""en""ru""uk""es"Request Body
Responses
The saved row
Regenerate an update translation
Re-runs machine translation of one update into lang and stores it as source: "machine". A hand-edited row is only replaced with force=1. Campaign owner only. Audit-logged.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID.
"uuid""uuid""en""ru""uk""es"Query Parameters
1 or true overwrites a hand-edited row.
"1""true"Responses
The regenerated row
List a legacy update's translations
Legacy. Campaign updates are translated through GET /fundraisers/{id}/updates/{updateId}/translations; use that instead.
Returns the update's source text and one row per other locale. The caller must own the parent campaign.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Source and translations
Save a legacy update translation
Legacy (see GET /updates/{updateId}/translations). Saves a hand-edited translation as source: "human". Owner of the parent campaign only. Audit-logged.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid""en""ru""uk""es"Request Body
Responses
The saved row
Endorsements
Ambassadors publicly vouching for other people's campaigns, and creators asking ambassadors to promote theirs.
List a campaign's endorsers
Who stands behind a campaign: everyone who endorsed it, plus everyone whose share link for it recorded a visit, minus the organizer and profiles set to private. Ordered by impressions (human visits through their link). Authentication is optional; with a session, viewerHasEndorsed says whether the caller is on the list. A deleted campaign returns an empty list.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID; anything else answers 400.
"uuid"Responses
Endorsers (unwrapped)
Endorse a campaign
Publicly vouches for a campaign as the signed-in user, with an optional note. Idempotent: endorsing again updates the note and answers 200 with created: false; a new endorsement answers 201 and notifies the campaign owner.
Only public campaigns that are active, paused or ended can be endorsed, and not your own.
Requires permission: endorse_campaigns
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID; anything else answers 400.
"uuid"Request Body
Responses
Existing endorsement updated
Withdraw an endorsement
Revokes the caller's live endorsement of the campaign and notifies the owner.
Requires permission: endorse_campaigns
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID; anything else answers 400.
"uuid"Responses
Revoked
List a campaign's endorsement requests
Who the owner has asked to promote the campaign, newest first, and whether each has been emailed yet. Campaign owner only.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID; anything else answers 400.
"uuid"Responses
Requests (unwrapped)
Ask ambassadors to promote a campaign
Asks up to 12 ambassadors at once to promote the caller's campaign. Campaign owner only; no special permission is needed. Requires a verified email address.
Ids that are not current ambassadors (and the caller's own id) are dropped and listed in rejected. When the campaign is public and active, the ambassadors are emailed now (status: "sent"); otherwise the requests are queued and sent when the campaign is approved (status: "queued"). Asking the same ambassador again refreshes the request rather than duplicating it.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's UUID; anything else answers 400.
"uuid"Request Body
Responses
Requests recorded
Images
Stock-photo search, AI cover generation and server-side image copying for the campaign builder, plus an allowlisted image proxy.
Proxy an allowlisted image
Streams an image from the platform's CDN or images.unsplash.com, for local development. Public. The upstream body and Content-Type are passed through with a 24-hour cache header; the upstream status code is not, so an upstream error page also arrives as 200.
Parameters
Query Parameters
"uri"Responses
The upstream image
Which image features are available
Search stock photos
Searches Unsplash for landscape photos. Requires a bearer session. When a photo is chosen, call POST /images/track-download with its downloadLocation, as Unsplash's terms require.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
1113020Responses
Search results
Generate a cover image with AI
Generates a landscape campaign cover from a prompt (the first 500 characters are used, wrapped in a fixed style prompt). Requires a bearer session.
With the default gpt-image-* model the image is stored on the platform CDN immediately and persisted is true; that path is also gated by features.image_uploads and answers 403 when the flag is off. With a dall-e-* model the URL is OpenAI's temporary one (persisted: false) and should be copied with POST /images/save-from-url.
Limited to 10 generations an hour and 30 a day per user; past either limit the answer is 429 with Retry-After. Errors from the image provider are not passed through: a prompt the provider refuses answers 400, a temporarily unavailable provider 503, and any other provider failure 502, each with a generic message.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Generated image
Make a photo square with AI
Re-renders a photo as a square without cutting anyone out of it: the people and the setting are kept and the rest of the frame is composed to fill the square. A photo that is already square is rendered again, so calling twice gives a second rendering. Requires a bearer session.
The square comes back as base64 and nothing is stored. Upload it through the normal image upload to use it as a cover.
Shares the limits of POST /images/generate: 10 an hour and 30 a day per user; past either limit the answer is 429 with Retry-After. Errors from the image provider are not passed through: a photo the provider refuses answers 400, a temporarily unavailable provider 503, and any other provider failure 502, each with a generic message.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
The square photo
Record a stock-photo selection
Tells Unsplash a photo was chosen, as its API guidelines require. Pass the downloadLocation from GET /images/search. Never fails the caller's action: a failed ping answers 200 with tracked: false. Requires a bearer session.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Tracking outcome
Copy a generated image to the CDN
Downloads an AI-generated image from OpenAI's image storage and stores it on the platform CDN, returning the permanent URL. A URL already on the platform CDN is returned unchanged. Requires a bearer session and the features.image_uploads flag.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Stored image
Media
A campaign's photo and video gallery. Behind the features.fundraiser_video flag.
List a campaign's media
The campaign's photos and videos in gallery order. Every reader sees the public list (DMCA-taken-down items removed); the owner also sees processing and taken-down items. Answers 403 for everyone while features.fundraiser_video is off. Readable exactly when GET /fundraisers/{id} is: a campaign that is not active, paused or ended, is private, or has been deleted answers 404 to everyone but its owner.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Media items
Add media to a campaign
Adds one item, by kind:
image— a URL fromPOST /storage/upload(other hosts are
rejected). The first image becomes the cover unlessisCoversays
otherwise. At most 10 images per campaign.video_link— a YouTube, Vimeo, Wistia, Loom, Instagram, Facebook
or TikTok URL. At most 3 videos per campaign.video_upload— reserves a direct upload to the video host and
returns its one-timeuploadURL. Counts toward the 3 videos.
Campaign owner only. Behind features.fundraiser_video and the media-upload rate limit (30 per 15 minutes). Audit-logged.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Request Body
Responses
Item created
Preview a video link
Parses a video URL and returns what the gallery would embed, for the builder's preview card. Nothing is saved. Thumbnail and title come from the provider's oEmbed and may be null. Campaign owner only; behind features.fundraiser_video.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Request Body
Responses
Parsed link
Reorder a campaign's media
Sets the gallery order to the order of ids. Every id must belong to this campaign. Campaign owner only; behind features.fundraiser_video. Audit-logged.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Request Body
Responses
Reordered
Set the cover image
Makes one image the campaign's cover (clearing the previous one). Only images can be the cover. Campaign owner only; behind features.fundraiser_video. Audit-logged.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid""uuid"Responses
The new cover
Remove a media item
Removes one item from the gallery. Campaign owner only; behind features.fundraiser_video. Audit-logged.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid""uuid"Responses
Removed
Achievements
The public badge catalogue, single badges, the "just earned" feed, and individual earned cards with their share pictures and verify QR codes.
Operations
Get the badge catalogue
Every badge on the platform with its copy, tier ladder, holder counts and rarity, plus header totals, series and the grid's slots, in the reader's language (?lang=, then Accept-Language, then the i18next cookie). The payload is the same for every reader; a token is accepted and ignored. Cached per language.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
Reader language; falls back to Accept-Language, then the i18next cookie, then English.
"en""ru""uk""es"Responses
Catalogue (bare object)
Get an achievement art sheet image
The image of one badge art sheet, served immutably (one year) under its version token ?v=. A request without the current token is redirected (302, uncached) to the current versioned URL. Public.
Parameters
Path Parameters
"uuid"Query Parameters
Anything else is the full sheet.
"full""small""cutout""cutout_small""full"Version token. Cut-out sizes append -c.
Responses
The image (its stored content type)
Get an achievement library image
One image from the badge art library, served immutably under its version token ?v=; a request without the current token is redirected (302) to it. Public.
Parameters
Path Parameters
Query Parameters
"full""small""full"Responses
The image (its stored content type)
Get the achievement vocabulary
The labels and colours every badge card is drawn with (tiers, tracks, rarity frames and so on), in the reader's language. Public.
Parameters
Query Parameters
Reader language; falls back to Accept-Language, then the i18next cookie, then English.
"en""ru""uk""es"Responses
Vocabulary
Get the "just earned" feed
The most recent cards issued or upgraded, platform-wide or for one badge (species). The size, maximum age and suggested refresh interval come from platform settings. An unknown, hidden or private badge answers an empty list. Public; sent with Cache-Control: no-store.
Parameters
Query Parameters
A badge slug. Repeating the parameter answers an empty list.
Reader language; falls back to Accept-Language, then the i18next cookie, then English.
"en""ru""uk""es"Responses
Feed
Get one badge
One badge with its copy, ladder and rarity. Authentication is optional: a signed-in reader also gets their own standing (earned, tier, progress). With holder_type and holder_id, the badge is answered for that holder instead (a profile's or organization's badge); both must be given together.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Query Parameters
"user""organization""uuid"Reader language; falls back to Accept-Language, then the i18next cookie, then English.
"en""ru""uk""es"Responses
The badge
Get a badge share picture
A PNG of the badge for sharing: og 1200×630, post 1080×1080, story and reel 1080×1920. A trailing .png on asset is accepted. ?tier= draws it at that tier when the tier exists. Drafts and private badges answer 404. Public; cached for 5 minutes.
Parameters
Path Parameters
"og""post""story""reel""og.png""post.png""story.png""reel.png"Query Parameters
Reader language; falls back to Accept-Language, then the i18next cookie, then English.
"en""ru""uk""es"Responses
PNG image
Get an earned card
One earned card by its short code, as the card's public page shows it: the badge, holder, tier, dates, serial, evidence, ladder, rarity, the verify URL and share links. Authentication is optional; the holder's own session adds an owner block. Sent private, no-store.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The card's short code.
Query Parameters
Reader language; falls back to Accept-Language, then the i18next cookie, then English.
"en""ru""uk""es"Responses
The card
Get an earned card's share picture
A PNG of one earned card: og 1200×630, post 1080×1080, story 1080×1920 (a trailing .png is accepted). Served only when the card is readable by a stranger. When the card's own picture is not rendered yet, the badge's generic picture is served with X-Card-Render: pending and no-store. Supports If-None-Match (304). Public; a per-IP limit (120 per minute by default) applies in addition to the public browsing limit.
Parameters
Path Parameters
The card's short code.
"og""post""story""og.png""post.png""story.png"Query Parameters
Responses
PNG image. Content-Language names the language it was drawn in.
Get an earned card's verify QR code
An SVG QR code pointing at the card's verify URL. Only for cards readable by a stranger. Public; same per-IP limit as the share pictures. Cached for 60 seconds.
Parameters
Path Parameters
The card's short code.
Responses
SVG image
Get a profile's achievements
The achievements section of a profile: the badges the person has earned that visitors may see, each with its card, plus a public holder block, a summary counted over the returned rows, the profile's pinned card and the label vocabulary. id is a UUID or a profile slug.
The answer is the same for everyone, the owner included, except that the owner's own rows carry their counts (stats). A private or inactive profile read by anyone but its owner answers { "data": [], "withheld": true, "vocabulary": … }.
Authentication is optional. Rate limited at 300 requests/minute per IP.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
Profile UUID or profile slug.
Query Parameters
Locale for titles and labels; unsupported locales fall back to English.
Responses
The collection, or a withheld answer
Get my achievements
The owner view: every achievement the caller holds — themselves and through organizations they administer — including those set to "only me", with private stats and progress; next_up, the closest badges not yet earned; the caller's pin; and how many new cards are waiting to be revealed.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
Responses
The owner collection
Pin an achievement card
Pins one of the caller's own live cards (theirs, or an organization's they administer) to that holder's profile. A malformed, unknown, void or someone else's card all get the same 404 body, so the answer never says whether a code exists. Responses are private, no-store.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Pinned
Unpin an achievement card
Clears the caller's pin, or with organization that organization's pin when the caller administers it. Idempotent. The profile then shows the automatic choice.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
"uuid"Responses
Unpinned
Get my reveal pack
Cards waiting to be "opened": with receipt, the ones a particular donation earned (the thank-you page — status: pending while the award is still being computed); with all=true, every unrevealed card. Pass exactly one. Someone else's receipt answers like a donation that earned nothing (status: none). Responses are private, no-store.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
A Stripe PaymentIntent id.
"^pi_[A-Za-z0-9_]{1,255}$""true"Responses
The pack
Mark reveal events as opened
Marks the caller's own reveal events as revealed. Ids that are not the caller's are ignored silently; the answer is 204 either way.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Acknowledged
Set an achievement's visibility
The holder's "Visible to: Everyone / Only me" control for one badge. default follows the badge's own visibility. When the caller wears the badge both personally and through an organization (or through two organizations), holder must say which; otherwise 409 with the list of holders.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The achievement's slug.
Request Body
Responses
The updated award
Upload an image
Stores a base64-encoded raster image on the platform CDN and returns its public URL, which the media gallery, outcome reports and the campaign form accept. Requires a bearer session. Only raster image types are accepted (no SVG or HTML), into one of four folders. Omit path and the server stores the file under a new unique path beginning with the caller's user id, returned as path. A supplied path must begin with the caller's own user id, profile id or Cognito sub, the same rule POST /storage/delete applies. An upload never replaces an existing file. The JSON body limit (10 MB) caps the file size.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Stored
Delete an uploaded image
Deletes one uploaded file. The path's first segment must be the caller's own user id, profile id or Cognito sub; files uploaded under any other prefix cannot be deleted through this endpoint. Requires a bearer session.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Deleted
API Keys
Long-lived fh_live_… keys for CLI and agent access. A key is sent as Authorization: Bearer fh_live_… and authenticates as the user who created it, on every endpoint that accepts a bearer session.
List my API keys
The authenticated user's keys, newest first. Never includes the secret. Revoked keys are excluded unless include_revoked=true.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
"true""false""false"Responses
The caller's keys
Create an API key
Mints a new API key for the authenticated user. The full key is returned once, in api_key, and cannot be retrieved again. key.prefix (the first 12 characters) is what later listings show.
Requires a signed-in session (the session cookies, or a Cognito JWT in the Authorization header). A request authenticated with an API key, or made while an administrator is viewing the account as its owner, answers 403.
Every new key expires: one year after creation unless expires_at asks for sooner. A key stops working early if it is revoked, if its owner's account is suspended, banned or deactivated, or if an administrator signs the owner out of every device (which revokes all of the owner's keys).
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Key created
Revoke an API key
Revokes one of the caller's own keys. Takes effect immediately. A key that belongs to someone else, does not exist, or is already revoked answers 404.
Requires a signed-in session: a request authenticated with an API key, or made while an administrator is viewing the account as its owner, answers 403.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Key revoked
Meta
Crawler-facing discovery files (llms.txt, sitemaps), generated from live data and proxied by the frontend at the site's public paths.
Get llms.txt
The AI-crawler discovery file served at https://fundlyhub.org/llms.txt: live platform statistics, categories, trending campaigns, key pages and localised versions, in Markdown. Cached for an hour. No authentication.
Responses
The file
Get llms-full.txt
Get the sitemap index
Get a locale sitemap
The <urlset> for one locale, with hreflang alternates. Accepts the bare locale or the locale with .xml. Cached for five minutes. No authentication.
Parameters
Path Parameters
"en""ru""uk""es""en.xml""ru.xml""uk.xml""es.xml"Responses
Sitemap XML
Creator Subscriptions
Creator monetisation tiers (each mirrored to a Stripe Product and Prices) and the fan-side recurring subscriptions to them.
List a creator's tiers
A creator's active tiers, by sort_order then price. Empty for a private profile, except to its owner.
Authentication is optional; it only matters for the owner.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The creator's profile UUID (slugs are not accepted).
"uuid"Responses
Active tiers
List my tiers
The caller's tiers, archived ones included (active first). Empty for someone who has never created one.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
The caller's tiers
Create a tier
Creates a tier and its Stripe Product with a monthly Price (and an annual one when annual_amount_cents is given). Creating a first tier grants the caller the creator role; there is no separate "become a creator" step. Receiving payouts still needs Stripe Connect onboarding — fans can subscribe before then, and the money is held.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Tier created
Archive a tier
Archives one of the caller's tiers (is_active: false), hiding it from the public list. Reversible, and safe for tiers with subscribers. To remove a tier for good, use DELETE /me/tiers/{tier_id}/permanent.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Archived
Update a tier
Partial update of one of the caller's tiers. Changing a price mints a new Stripe Price and deactivates the old one; existing subscribers keep their original price until renewal. Setting annual_amount_cents to null withdraws annual billing. Currency cannot be changed. A tier that is not the caller's is a 404.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Request Body
Responses
Updated tier
Delete a tier permanently
Deletes one of the caller's tiers outright. Only possible for a tier nobody has ever subscribed to — a cancelled subscription is still a billing record — otherwise 409 with code: TIER_HAS_SUBSCRIBERS; archive it instead.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Deleted
List my creator subscriptions
Every subscription the caller holds, ended ones included, with tier and creator display fields. Active and trialing first, then past due, paused, and the rest; newest first within each.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
The caller's subscriptions
Subscribe to a creator tier
Creates a Stripe Subscription to an active tier, recorded locally as incomplete. Confirm the first payment in the browser with Stripe Elements using client_secret; the webhook then moves it to active.
Calling again while a previous attempt for the same tier is still incomplete or past_due resumes it — the same subscription and client_secret — rather than creating a second one. An active or trialing subscription to the tier is a 409. Subscribing to a different tier of the same creator is allowed.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Subscription created or resumed
Cancel a creator subscription
An active or trialing subscription is set to cancel at the end of the paid period. One that is incomplete, past_due, unpaid or paused is cancelled immediately. Already cancelled or already scheduled is a no-op. A subscription that is not the caller's is a 404.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The local subscription id (subscription_id), not the Stripe id.
"uuid"Responses
Cancelled or scheduled
Resume a creator subscription
Undoes a scheduled cancellation while the subscription is still running. A no-op when nothing is scheduled. A subscription that has already ended cannot be resumed — subscribe again.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
"uuid"Responses
Resumed (or nothing to undo)
Email Preferences
Public, token-authorised unsubscribe and resubscribe for any address FundlyHub mails, including guest donors with no account.
Preview an unsubscribe link
Validates the signed token from an email's unsubscribe link and says which (masked) address and which scope it would silence. Changes nothing.
No authentication: the signed token is the authorisation. Rate limited at 300 requests/minute per IP.
Parameters
Query Parameters
Responses
The token is valid
Unsubscribe
Suppresses mail to the token's address for the token's scope, or for everything when all is true. Idempotent.
No authentication: the signed token is the authorisation. No feature flag can switch this off. Rate limited at 10 requests/minute per IP.
Request Body
Responses
Unsubscribed
Resubscribe
Lifts the suppression for the token's address and scope (or for all). The undo for POST /unsubscribe.
No authentication: the signed token is the authorisation. Rate limited at 10 requests/minute per IP.
Request Body
Responses
Resubscribed
Request a fresh unsubscribe link
Mails a new signed unsubscribe link to email — the repair path for old emails whose link carried no token. Sent only to an address FundlyHub has previously mailed, at most once per address per hour.
Always answers { "data": { "sent": true } }, whatever happened, so it cannot be used to test whether an address is known.
No authentication. Rate limited at 10 requests/minute per IP.
Request Body
Responses
Accepted
Ambassadors
The ambassador programme: the public directory, invitations and applications, referral links and click tracking, and the ambassador portal under /me/referrals/*.
Operations
Redeem an ambassador invitation
Accepts an ambassador invitation for the signed-in account — the path for Google and Apple sign-ups, which cannot carry the token through POST /cognito/signup. The invitation is matched against the account's own registered email address; someone else's token is declined with email_mismatch.
A declined redemption is still a 200 with granted: false and a reason, so a client can always call this after sign-up even when the token was already spent. No special permission is needed.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Granted, or declined with a reason
Apply to be an ambassador
Files an application to the ambassador programme for review by FundlyHub. Works signed out; when a session is present, the application records which account filed it.
One pending application per address: a second is 409, as is an address that already holds the ambassador role.
Authentication is optional. Rate limited at 5 requests/minute per IP.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Application received
List ambassadors (public directory)
Ambassador role-holders with a public profile, ordered by the money their referral links have driven (the amounts themselves are not returned). Feeds the front-page rail.
No authentication. Rate limited at 300 requests/minute per IP.
Parameters
Query Parameters
16024Responses
The directory
List ambassadors for the endorsement picker
Ambassadors a campaign creator may ask to endorse their campaign: role-holders with a public profile, excluding the caller, with the lifetime money each has driven in cents. Authenticated because it carries those amounts.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
16024Responses
The picker list
Get an ambassador's referral analytics
All-time referral analytics for one ambassador: clicks by traffic type, conversions, attributed funds per currency, and breakdowns by campaign, UTM source and day.
Callers may read their own analytics; anyone else's answers 403.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The ambassador's profile UUID.
"uuid"Responses
The analytics (bare object)
Get or create my referral code
Returns the caller's referral code — the profile-level one, or the one for fundraiser_id — creating it on first use. Idempotent. Share links take the form /r/{code} on the site. Every signed-in user can hold a code; the ambassador role is not required.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
The code
Record a referral-link click
Called by the FundlyHub web app when a visitor opens a referral link (/r/{code}); not for third-party clients. Returns where to redirect and the visitor id to set as a cookie. Records a click classified as human, bot or unknown, and stamps the code into utm_content. When the visitor is signed in, the code is also remembered on their profile for attribution.
An unknown code still answers 200 with resolved: false and a target_path of the home page.
Authentication is optional. Rate limited per caller: 120 requests/minute, on top of the global per-IP limiter. Past the limit the answer is 429 with Retry-After.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Request Body
Responses
Click processed
Get my referral summary
The ambassador portal's headline: totals, the click-to-gift funnel and a daily series for the caller's own referral links over a window (default: the 28 days ending to). Every portal route reads only the session's own rows.
Requires permission: view_own_referral_portal.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
Window start. Defaults to 28 days before to. Must be before to and not earlier than 2020-01-01.
"date-time"Window end. Defaults to now.
"date-time"Responses
The summary
List campaigns I referred to
Per-campaign clicks, visitors, driven gifts and money raised from the caller's referral links in the window.
Requires permission: view_own_referral_portal.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
Window start. Defaults to 28 days before to. Must be before to and not earlier than 2020-01-01.
"date-time"Window end. Defaults to now.
"date-time"Responses
The campaigns
Get what my referral link did for one campaign
All-time figures for the caller's own referral link on one campaign: impressions (visits not identified as a bot), clicks by traffic type, distinct human visitors, driven gifts (paid, not self-referred), distinct donors, and the value of those gifts (donation + tip) per currency in cents. The same definitions as the per-campaign rows of the ambassador analytics. Also returns the caller's referral code and link for the campaign when one exists; this read never creates one. Feeds the ambassador bar on the campaign page.
A campaign the caller may not open (draft, pending, private or deleted, and not their own) is a 404, as its page is.
Requires permission: view_own_referral_portal.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The campaign's id.
"uuid"Responses
The caller's figures for the campaign
Get my referral traffic breakdown
Where the caller's referral clicks came from — by UTM source, referer and medium — plus traffic type, and country and device splits from Google Analytics when it is configured (ga_note says why those are null otherwise).
Requires permission: view_own_referral_portal.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
Window start. Defaults to 28 days before to. Must be before to and not earlier than 2020-01-01.
"date-time"Window end. Defaults to now.
"date-time"Responses
The breakdown
List gifts I drove
The donations attributed to the caller's referral links in the window, refunded, failed and self-referred gifts included. Each row carries exactly the fields of AmbassadorGift: no donor email, card details or receipt reference, and no donor name on an anonymous gift.
Requires permission: view_own_referral_portal.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
Window start. Defaults to 28 days before to. Must be before to and not earlier than 2020-01-01.
"date-time"Window end. Defaults to now.
"date-time"12005000Responses
One page of gifts
Get my ambassador standing
The caller's rank among ambassadors by money raised in the window, and the leaderboard (up to 200 rows; truncated when there are more). Other ambassadors with private profiles appear without name or avatar.
Requires permission: view_own_referral_portal.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Query Parameters
Window start. Defaults to 28 days before to. Must be before to and not earlier than 2020-01-01.
"date-time"Window end. Defaults to now.
"date-time"Responses
The standing
List my own campaigns (ambassador portal)
The campaigns the caller owns, with totals and how many clicks on their own referral links led to them (self-referrals, which do not count as driven). Takes no date range.
Requires permission: view_own_referral_portal.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
The caller's campaigns
DMCA
DMCA §512 takedown notices and counter-notices. Currently dark behind the features.dmca_workflow flag.
File a DMCA takedown notice
Files a §512(c) takedown notice against a campaign or one of its media files. No account is needed to file. Both attestations must be true, and either mediaId or fundraiserId is required. Returns only a reference id; the claimant is emailed a confirmation and updates.
No authentication. Behind features.dmca_workflow, which is currently disabled (403). Rate limited at 5 requests/minute per IP.
Request Body
Responses
Notice received
File a DMCA counter-notice
The uploader of media taken down under a notice files a §512(g) counter-notice. Only the media's original uploader may file, and only against a complaint in valid status. All three statements must be true. FundlyHub forwards it to the claimant.
Behind features.dmca_workflow, which is currently disabled (403).
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Parameters
Path Parameters
The complaint id.
"uuid"Request Body
Responses
Counter-notice received
Get the live-chat identity hash
The caller's identity hash for the support chat widget's setUser(…, { identifier_hash }), so the chat knows which signed-in user it is talking to.
Authorizations
In the browser, authentication rides on the httpOnly session cookies set by /cognito/signin or the Google / Apple sign-in at /cognito/oauth/{provider}. For scripts and for Swagger UI testing, paste an API key (fh_live_…, created with POST /api-keys); a Cognito JWT is accepted too. Sign-in does not return a token in its body.
Responses
The hash