Domains
Donations
Fetch donation campaigns and accept donations against uidu's fundraising API.
- Use it for: Public website · Server integration
- Reads: public token — works in the browser
- Writes: Bearer (
apiKey) — server-side only - CLI:
uidu donations - Auth: what each token can do and where it may run — Authentication
Donation campaigns are your workspace's fundraisers — a charity drive, a sponsorship pool, a
crowdfunding goal. You show them and their progress with the public token, and record each gift
with createDonation from your server.
Mental model
DonationCampaign— the fundraiser:name, a rich-textbody(Tiptap JSON — render it with<RichText>),cover,kind,startsAt,publishedAt,publicPath/publicUrl, aprimaryAddress, and live totals:goal,donationsAmount,donationsCount,donationsAverage. It may carry aform— a donor questionnaire with the same shape as Forms — for anything beyond name and email.Donation— one gift to one campaign (donationCampaignId):amount,currency, the donor as a contact, and optionally a message (body), adedication("in memory of…") and the answers to the campaign's form (formResponseAttributes). It comes back withid,amount,currency,createdAtandcontact.id.
Shapes and gotchas:
- Money is in cents.
goal,donationsAmount,donationsAverageand a donation'samountare integers in the currency's minor unit:5000is 50.00. Divide by 100 to display, multiply by 100 (and round) before you write.currencyis an ISO code:'EUR','USD'… - Recording is not charging.
createDonationrecords the gift and the donor; taking the money is a separate step with your payment processor (Stripe, …), unless payment runs through uidu's checkout (orderAttributes/subscriptionAttributes, below). - A gift is not a campaign.
createDonationrecords a gift to a campaign;createDonationCampaigncreates the campaign itself. - Both mutations return the payload as-is —
{ errors, donation }/{ errors, donationCampaign }, or null — with validation failures inerrors, not thrown. Both need theapiKeyclient: a donation is submitted from a server action, never with the public token in the browser. See Writes and Authentication.
Use cases
Campaigns page with progress bars
Goal: list the active campaigns, each with how much it has raised against its goal.
// app/donate/page.tsx — a Server Component
import Link from 'next/link';
import { listDonationCampaigns } from '@uidu/client';
import { uidu } from '@/lib/uidu';
const euros = (cents: number | null | undefined) =>
((cents ?? 0) / 100).toLocaleString('en', {
style: 'currency',
currency: 'EUR',
});
export default async function CampaignsPage() {
const campaigns = await listDonationCampaigns(uidu, { first: 20 });
return (
<ul>
{campaigns.map((c) => {
const pct = c.goal
? Math.min(100, ((c.donationsAmount ?? 0) / c.goal) * 100)
: 0;
return (
<li key={c.id}>
<Link href={`/donate/${encodeURIComponent(c.id)}`}>{c.name}</Link>
<div className="h-2 rounded-full bg-slate-200">
<div
className="h-full rounded-full bg-emerald-500"
style={{ width: `${pct}%` }}
/>
</div>
<p>
{euros(c.donationsAmount)} raised of {euros(c.goal)} ·{' '}
{c.donationsCount ?? 0} donors
</p>
</li>
);
})}
</ul>
);
}- Summaries are cheap:
id,name,cover,goal,donationsAmount,donationsCount,startsAt,publishedAt,publicPath. Thebody,donationsAverageand the donorformcome only fromgetDonationCampaign. - A campaign without a
goalhas no bar to fill — guard the division, as above.
Campaign page with a donation form
Goal: show one campaign and let a visitor give an amount, with an optional message.
// app/donate/[id]/page.tsx
import { createDonation, getDonationCampaign } from '@uidu/client';
import { RichText } from '@uidu/react';
import { notFound } from 'next/navigation';
import { uidu } from '@/lib/uidu'; // createClient({ workspace, publicToken, apiKey })
export default async function CampaignPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const campaign = await getDonationCampaign(uidu, {
id: decodeURIComponent(id),
});
if (!campaign) notFound();
const campaignId = campaign.id;
async function donate(formData: FormData) {
'use server';
const message = String(formData.get('message') ?? '');
const result = await createDonation(uidu, {
input: {
attributes: {
donationCampaignId: campaignId,
amount: Math.round(Number(formData.get('amount')) * 100), // euros in, cents out
currency: 'EUR',
contactAttributes: {
email: String(formData.get('email')),
contactableAttributes: {
kind: 'person',
firstName: String(formData.get('firstName')),
},
},
body: message
? {
type: 'doc',
content: [
{
type: 'paragraph',
content: [{ type: 'text', text: message }],
},
],
}
: undefined,
},
},
});
if (result?.errors?.length) return; // return result.errors to the form
// result?.donation?.id, result?.donation?.amount (cents) — now charge with your processor
}
return (
<article>
<h1>{campaign.name}</h1>
{campaign.body && <RichText doc={campaign.body} />}
<form action={donate}>
<input name="amount" type="number" min="1" step="0.01" required />
<input name="firstName" required />
<input name="email" type="email" required />
<textarea name="message" />
<button>Donate</button>
</form>
</article>
);
}- The donor.
contactAttributescreates the donor's contact in the same mutation — a new contact on every gift unless you pass theidof an existing one inside it (contactAttributes: { id }).DonationAttributesalso hascontactId. - Message and dedication are JSON — typically a Tiptap document, as above.
- The campaign's form. When
campaign.formis set, render it like any form (its questions are atcampaign.form.inputs) and send the answers asformResponseAttributes: { formId: campaign.form.id, fieldValuesAttributes: [...] }— eachcontentis{ value }, see Forms. - Paying through uidu's checkout.
createDonation's input also takesorderAttributes(a one-time payment) orsubscriptionAttributes(recurring billing), next toattributes. With either, the donation'samountis computed from the order's or the plan's price, not from theamountyou send. The attributes they expect depend on how checkout is configured in your workspace — see Schema forOrderAttributesandSubscriptionAttributes. - The demo's version, with error handling, is
submitDonationinapps/demo/src/lib/actions.ts.
Launch a campaign from your server
Goal: create a fundraiser with a goal from a script or an admin tool.
import { createDonationCampaign } from '@uidu/client';
import { uidu } from '@/lib/uidu'; // must carry apiKey
const result = await createDonationCampaign(uidu, {
input: { attributes: { name: 'Winter appeal', goal: 500000 } }, // €5,000.00 in cents
});
if (result?.errors?.length)
throw new Error(result.errors.map((e) => e.name).join(', '));
result?.donationCampaign?.id;DonationCampaignAttributesalso takesbody,abstract,cover,kind,category, start and end dates andpublishedAt— see Schema. Change it later withupdateDonationCampaign, remove it withdeleteDonationCampaign.- The same from a terminal:
uidu donations create | update | delete— CLI.
API at a glance
Every @uidu/client function in Donations, generated from the source — the full reference has the rest of the SDK.
| Function | Kind | Auth | CLI | Description |
|---|---|---|---|---|
createDonation | write | Bearer | — | Record a donation (amount in cents) to a campaign, creating the donor's contact inline; returns { errors, donation } as-is. |
createDonationCampaign | write | Bearer | donations create | Create a donation campaign (goal in cents); returns the payload as-is, { errors, donationCampaign }. |
deleteDonationCampaign | write | Bearer | donations delete | Delete a donation campaign by { input: { id } }. Returns the payload (with errors) or null. |
getDonationCampaign | read | public token | donations get | One donation campaign with its body, its totals (goal, donationsAmount, in cents) and its donor form, or null. |
listDonationCampaigns | read | public token | donations list | The workspace's donation campaigns as a flat array of summaries with their totals in cents; page with first/after. |
updateDonationCampaign | write | Bearer | donations update | Update a donation campaign: { input: { id, attributes } }. Returns the payload (with errors) or null. |
Types: DonationCampaign
CLI:
| Entity | list | get | create | update | delete |
|---|---|---|---|---|---|
donations | ✓ | ✓ | ✓ | ✓ | ✓ |