Fundraise Up Documentation Audit
The docs cover a broad surface (REST API, JS API, URL API, dashboard tooling, ~16 integrations) but the developer-facing layer is fragmented across two hosts, has cross-API parameter inconsistencies for the same concept (test mode), ships at least one example URL with an undefined parameter, and contains a flat-out integrations contradiction between the Get Started and Integrations pages. Below are concrete, evidence-backed findings.
1. REST API docs live on a different host with broken deep links (critical)
Location: /docs/rest-api/ → 302 redirect to https://api.fundraiseup.com/v1/docs/; deep link https://api.fundraiseup.com/v1/docs/operations/POST-donations
Problem: The "REST API" entry from the documentation index does not actually serve content at fundraiseup.com/docs/rest-api/ — it 302-redirects to a separate Stoplight instance at api.fundraiseup.com/v1/docs/. Worse, attempting to deep-link to a specific operation on that instance, e.g. …/operations/POST-donations, returns the literal JSON {"error":{"message":"Resource not found.","code":"not_found"}}. The Stoplight surface is a single-page app with no working operation-level URLs.
Consequence: Search engines, AI coding agents, and humans linking from Stack Overflow / GitHub issues cannot link to "the create-donation endpoint" — every link either rebounds to the Overview or 404s. Agents that try to fetch individual endpoint specs programmatically receive a JSON error and silently treat the API surface as undocumented. The host split also means llms.txt, sitemaps, and site search at fundraiseup.com don't index the actual reference content.
The fix: Either (a) host the REST API reference under fundraiseup.com/docs/rest-api/ with stable per-endpoint URLs and an exposed OpenAPI spec, or (b) keep Stoplight but enable proper SSR / static rendering so /v1/docs/operations/POST-donations returns content rather than not_found. Publish the OpenAPI/Swagger JSON at a fixed URL (e.g. api.fundraiseup.com/v1/openapi.json) and link it from llms.txt.
2. URL API example uses an undocumented form parameter (critical)
Location: /docs/url-api/
Problem: The "Available Parameters" table on the URL API page lists amount, currency, designationId, elementId, email, firstName, lastName, modifyAmount, modifyDesignation, recurring, successUrl, tributeHonoree, and _lang. The very next "Example URL" on the same page is:
https://ropsi.org/?form=FUNCPJTZZQR&firstName=Alex&lastName=Garcia&email=alex.garcia@example.com&amount=100¤cy=USD&recurring=once
The leading form=FUNCPJTZZQR parameter — which appears to be the campaign/form ID and is presumably required for the example to work — is never defined in the parameter table.
Consequence: A developer or agent copy-pasting the example will produce a URL whose primary "which campaign?" identifier is undocumented. They have no way to know whether the parameter is form, formId, campaignId, or elementId (which is documented as "Required for Donation Form embeds"). Either the example is wrong or the table is incomplete; both are bugs.
The fix: Add form to the parameters table with its purpose, format (e.g. FUN… 8-12 char ID), where to find the value in the dashboard, and when it is required vs elementId. If form is a deprecated alias, document that and update the example to the canonical name.
3. Get Started lists Blackbaud as an integration; Integrations page does not (critical)
Location: /docs/get-started/ vs /docs/integrations/
Problem: The Get Started page advertises "integrations with Salesforce, Blackbaud, and Zapier." The Integrations page lists 16 supported native integrations: Bloomerang, Bonterra EveryAction, DonorPerfect, Double the Donation Matching, HubSpot CRM, Kindful, Microsoft Dynamics 365 F&E, Neon CRM, Raiser's Edge NXT, Salesforce, Salesforce Agentforce for Education, Salesforce Agentforce for Nonprofits, Salesforce NPSP, Virtuous CRM, Virtuous G&C Import Write, Zapier. "Blackbaud" is not on the Integrations list. The closest is "Raiser's Edge NXT" — a Blackbaud product — but the docs nowhere explain that Raiser's Edge NXT is the Blackbaud integration.
Consequence: A nonprofit evaluating Fundraise Up because its CRM is Blackbaud (and which products like Raiser's Edge, eTapestry, or Financial Edge NXT are all under the Blackbaud umbrella) reads the Get Started page and assumes any Blackbaud product will work. They reach the Integrations page and don't find "Blackbaud," so they conclude the platform doesn't support them — or worse, they sign up assuming eTapestry/Financial Edge support that doesn't exist. Agents producing integration documentation propagate the "Blackbaud-supported" claim verbatim with no scoping.
The fix: On the Get Started page, replace "Blackbaud" with the specific supported product, e.g. "Raiser's Edge NXT (Blackbaud)." On the Integrations page, label Raiser's Edge NXT explicitly as a Blackbaud product and clarify which other Blackbaud products are or are not supported.
4. Three different parameter names for the same "test mode" concept (significant)
Location: /docs/url-api/, /docs/javascript-api/, and the REST API Overview at api.fundraiseup.com/v1/docs/
Problem: The same "use test mode, not live mode" toggle has a different name and casing in each API surface:
- URL API:
fundraiseupLivemode=no(camelCase, "no" string) - JavaScript API:
window.fundraiseup_livemode = false;(snake_case, boolean) - REST API: a separate
livemodeflag governs the Test/Live data permissions split documented on the API key creation page
There is no cross-reference page mapping these three names to the same underlying flag.
Consequence: A developer testing across all three surfaces has to remember three different identifiers and three different value types for one switch. A developer who applies one form's name to another surface gets no warning, only a silent "this didn't toggle" — there is no shared documentation table that would let them catch the mismatch.
The fix: Standardize on one canonical name (e.g. livemode boolean) across URL API, JS API, and REST API. Where backwards compatibility prevents that, add a single "Test mode" reference page that lists all three forms side by side with the exact value type each accepts, and link to it from each API page.
5. Test-mode API key permissions silently exclude three feature areas (significant)
Location: api.fundraiseup.com/v1/docs/ — "How to get API key" section
Problem: The Live-data permissions list includes seven categories: Donations, Recurring plans, Supporters, Events, Fundraisers, Donor Portal access links, and Designation configurations. The Test-data permissions list immediately below contains only four: Donations, Recurring plans, Supporters, Events. Fundraisers, Donor Portal access links, and Designation configurations are missing without any explanatory note.
Consequence: A developer who builds an integration that creates Fundraisers, generates Donor Portal access links, or reads designation configurations cannot test it end-to-end with a Test-mode key — they will be forced to develop against Live data or hit silent permission errors, neither of which they can predict from the docs. Agents generating integration scaffolding will produce code that works in Live but errors in Test, with no clue why.
The fix: Either (a) add the three missing permissions to Test mode, or (b) add an explicit callout: "Test mode does not support the following: Fundraisers, Donor Portal access links, Designation configurations. Develop these features against Live data with throwaway records, or contact support for sandbox access."
6. /llms-full.txt returns 404 and /llms.txt is marketing copy (significant)
Location: https://fundraiseup.com/llms-full.txt and https://fundraiseup.com/llms.txt
Problem: /llms.txt exists and serves a high-level marketing summary of Fundraise Up ("AI-driven donation platform … 600 real-time transactions per second"), but the companion /llms-full.txt — the standard location for the bundled, agent-ingestible full-text corpus — returns HTTP 404. The llms.txt that does exist is a product-positioning blurb, not a structured map of doc pages or endpoint specs.
Consequence: AI coding agents (Claude Code, Cursor, Copilot, etc.) that follow the llms.txt convention to bulk-index documentation cannot retrieve a single canonical, machine-readable representation of the docs. Combined with finding #1 (REST API on a separate Stoplight SPA), this means agents see roughly the marketing landing page and a navigation tree, not the actual integration surface.
The fix: Publish /llms-full.txt containing the full text of every page under /docs/ plus the REST API operation pages. At minimum, replace the current marketing-style /llms.txt with a structured index that lists each doc URL and a short description, in the canonical llms.txt format.
7. Donor Portal link expiration: 24 hours vs 1 minute, never reconciled (significant)
Location: /docs/donor-portal/ and /docs/seamless-donor-portal/
Problem: /docs/donor-portal/ describes access as "time-limited magic links (24-hour expiration) rather than traditional passwords." /docs/seamless-donor-portal/ describes the API-generated equivalent as: "Access links are valid for 1 minute after they are generated and must be used immediately." Neither page acknowledges the other or explains that these are two different mechanisms (email magic links vs API access links) — they read as direct contradictions about the same feature.
Consequence: A developer integrating the API-generated link reads "24 hours" on the conceptual page, builds a flow that emails the link to the supporter (a 24-hour window seems plenty), and is then surprised when production links expire in 60 seconds. The /seamless-donor-portal/ page does warn against email distribution, but only after the developer has already absorbed the "24-hour" mental model.
The fix: On both pages, add a single sentence distinguishing the two mechanisms: "Email magic links sent by Fundraise Up expire in 24 hours. API-generated access links (POST /v1/donor_portal/access_links/...) expire in 60 seconds and must be redirected to immediately." Cross-link the two pages.
8. REST API supports a much narrower set of payment methods than the platform, with no callout (significant)
Location: api.fundraiseup.com/v1/docs/ (Overview → Limitations) vs /docs/payment-methods/
Problem: The REST API Overview limits the API to "credit cards, debit cards, Apple Pay, Google Pay, and ACH Direct Debit." The platform-wide Payment Methods page lists five families of payment methods spanning Stripe, PayPal, and Gemini — including PayPal, Venmo, SEPA Direct Debit, Bacs Direct Debit, BECS, Canadian PADs, iDEAL, and cryptocurrency. None of these PayPal/SEPA/Bacs/BECS/PAD/iDEAL/crypto methods are supported by the REST API, but the Payment Methods page makes no mention of API scope.
Consequence: A developer reading /docs/payment-methods/ sees "PayPal, SEPA, BECS, crypto" in tabular form and reasonably assumes they can process any of them via POST /v1/donations. They build the integration, attempt a SEPA donation, and fail with no guidance — the limitation is buried on a different page on a different host.
The fix: On /docs/payment-methods/, add a column or callout indicating which methods are API-creatable vs Checkout-only. On the REST API Overview, link back to the platform-wide Payment Methods page so developers can see the full inventory and the API subset together.
9. recurring frequency enum ends in "etc." (significant)
Location: /docs/url-api/ (parameter table); also referenced from /docs/javascript-api/
Problem: The recurring parameter is documented as: "Donation frequency (once, daily, weekly, biweekly, monthly, quarterly, annual, etc.)". The trailing "etc." is the only enum description — no full list is given.
Consequence: Developers and agents have no way to know whether semiannual, yearly, bimonthly, or every_two_weeks are valid values; an agent generating a config UI cannot enumerate the options. Worse, since this parameter flows into both the URL API and the JS openCheckout({ donation: { recurring: ... } }) call, an unrecognized value fails silently or in an undocumented way.
The fix: Replace "etc." with the complete list of accepted strings, exactly as the backend validates them, and surface the same enum in the JS API and REST API references.
10. JS API openCheckout example uses an unexplained FUNXXXXXXXX placeholder (significant)
Location: /docs/javascript-api/
Problem: The primary code example begins FundraiseUp.openCheckout('FUNXXXXXXXX', { ... }). The page does not explain what FUNXXXXXXXX is, where to find the real campaign ID in the dashboard, what format/length to expect, or that the Xs are placeholder characters rather than literal text. The URL API page uses a different illustrative ID (FUNCPJTZZQR) without explanation either.
Consequence: An agent extracting this snippet will paste it verbatim into generated code; a junior developer will run it as-is and get either a no-op or a console error with no clear next step. Across the URL API and JS API, the same kind of identifier is shown with two different example strings, neither labeled as a placeholder.
The fix: Replace FUNXXXXXXXX with a clearly marked placeholder (e.g. <YOUR_CAMPAIGN_ID>) plus a one-line "find this in Dashboard > Campaigns > [campaign] > Settings" note, and document the ID format (prefix FUN, length, character set).
11. No stable, linkable OpenAPI URL anywhere in the docs (significant)
Location: api.fundraiseup.com/v1/docs/ (Stoplight UI) and /llms.txt
Problem: The REST API is published through a Stoplight UI whose chrome shows an "Export" affordance ("Fundraise Up API — Export — Overview"), but the docs nowhere publish a stable, linkable URL for the OpenAPI/Swagger document. The Overview's table of contents lists "Authentication", "Rate limits", "Errors", "Pagination", "Request IDs" — all conceptual sections — with no anchor pointing to a spec file. llms.txt does not reference one either.
Consequence: Code generators, IDE plugins, agent tooling, and Postman/Insomnia importers cannot deep-link to a structured definition of endpoints, request/response schemas, or error shapes. Even if a developer manages to use the Stoplight Export button, every consumer must re-export by hand and re-host it — there is no canonical URL to point at.
The fix: Publish the OpenAPI document at a stable URL (e.g. https://api.fundraiseup.com/v1/openapi.json), link to it prominently from the Overview, and reference it from llms.txt so coding agents can fetch it programmatically.
12. Elements page lists the same elements under multiple categories with no explanation (significant)
Location: /docs/elements/
Problem: The Elements page organizes 20+ elements into six categories (Buttons, Data Displays, Forms, Links, Lists, Overlays). Several elements appear under more than one category with no explanation:
- "Top Fundraisers" appears under both Data Displays and Lists
- "Recent Donations" appears under both Data Displays and Lists
- "Top Supporters" appears under both Data Displays and Lists
- "Social Proof" appears under both Data Displays and Overlays
The page does not state whether these are the same element shown twice, two distinct variants of the element, or a documentation bug.
Consequence: A developer asking "where does Recent Donations live?" gets two answers and no way to disambiguate. An agent generating a UI catalog (e.g. for a configuration tool) will either deduplicate incorrectly or list the same component twice. The category taxonomy stops being load-bearing the moment a single name appears in two boxes without comment.
The fix: Either deduplicate (one element, one category) or, if "Social Proof (Data Display)" and "Social Proof (Overlay)" really are different elements, give them distinct names and one-line descriptions explaining the difference.
13. April 2026 deprecation announced without a visible migration guide link (minor)
Location: api.fundraiseup.com/v1/docs/ — "Changes in API" → April 2026 entry
Problem: The changelog entry states: transaction_attempt.* events now include a payments array; the top-level donation and recurring_plan fields are deprecated and "will be removed on 2026-10-30." It tells developers: "See the migration guide." The scraped content shows the sentence but no anchor or URL — and the deprecation deadline is roughly 5 months away as of today (2026-05-08).
Consequence: Customers on the old shape have a hard cutover date but no in-page link to the migration steps. Agents summarizing "what do I need to change?" can quote the deprecation but cannot retrieve the migration playbook.
The fix: Make "the migration guide" a real link to a dedicated page that shows before/after JSON for transaction_attempt.* events, lists which fields move where, and gives a copy-pasteable diff for common consumers.
14. API keys "do not expire" with no rotation guidance, while human passwords rotate every 90 days (minor)
Location: api.fundraiseup.com/v1/docs/ ("How to get API key") vs /docs/security/
Problem: The API key creation flow states: "Your key is activated immediately and does not expire." There is no recommended rotation cadence, no documented revocation pattern, and no guidance for credential leakage response. Meanwhile, /docs/security/ requires that human "Password updates required every 90 days when 2FA and SSO aren't enabled." The asymmetry — humans rotate, machine credentials never do — is unexplained.
Consequence: Customers building automations have no security posture guidance for long-lived API credentials. A leaked API key remains valid indefinitely until manually revoked, and the docs neither flag this risk nor recommend rotation frequency. Compliance-driven buyers (the PCI/SOC 2 audience the security page targets) will ask about API key lifecycle and find nothing.
The fix: Add an "API key rotation" section to the Managing API keys page with a recommended rotation cadence (e.g. every 90–180 days), a step-by-step rotation procedure (create new, switch traffic, revoke old), and a leaked-credential incident playbook.
15. Email locale list silently drops 5 of the 23 UI locales (minor)
Location: /docs/languages/
Problem: The Languages page documents 23 UI locales and 18 email locales. The note "Email localization doesn't support region-specific dialects" partially covers the missing regional variants (en-CA, en-GB, fr-CA, pt-PT, es-US), but it does not explain why Chinese (Traditional) is missing entirely from email — that's not a regional dialect, it's an entirely different writing system from Chinese (Simplified).
Consequence: An organization with Traditional-Chinese-reading supporters configures their UI in zh-TW and reasonably assumes their receipt and recurring-plan emails will follow. They won't, and the page never says so explicitly.
The fix: Either add a per-locale table showing UI vs Email support side by side, or expand the note to: "Email localization doesn't support region-specific dialects, and Chinese emails are sent only in Simplified Chinese regardless of UI setting."
What they do well
- The changelog at
/docs/changelog/is dense, dated, and machine-readable — agents can extract a clear month-by-month picture of recent API additions (ACH support, fee endpoint, grouped_donation_id). /docs/payment-methods/cleanly maps each payment method to its provider (Stripe / PayPal / Gemini) and currency coverage in tabular form.- Security and PII pages give specific compliance versions (PCI DSS Level 1 4.0.1, SOC 2 Type II, ISO 27001, WCAG 2.2 AA) rather than vague claims.
Top 3 recommendations
- Unify the REST API surface with the rest of the docs — fix Stoplight deep links (or move off Stoplight), publish a stable OpenAPI JSON URL, and add
/llms-full.txt. This single change unblocks every coding agent that currently sees the REST surface as marketing copy. - Fix the cross-page contradictions that humans use judgment to navigate but agents fail on — Blackbaud-vs-Raiser's-Edge-NXT, the undocumented
formparameter in the URL API example, the three names for "test mode," the API-vs-platform payment method mismatch, and the 24-hour-vs-1-minute Donor Portal expiration. - Close the Test-mode permission gap — either grant Test keys the same Fundraisers / Donor Portal access link / Designation configuration permissions Live keys have, or document explicitly that those features cannot be exercised in Test and tell developers what to do instead.