Campaign Drafts Before Email Verification
A signed-in account whose email address is not yet confirmed can save and edit campaign drafts. It cannot publish, submit for review, or edit anything that has left draft. Verifying the email lifts every restriction on this page.
The exception exists so that an autosave never fails: the iOS app saves "New campaign" to the account when the screen is closed, whether or not the address has been confirmed. The website's builder keeps its own draft in the browser (see the guide) and is not affected.
Which requests are allowed
POST /fundraisers and PATCH /fundraisers/:id sit behind a draft-aware email gate instead of the plain verified-email gate. For a verified account the gate does nothing. For an unverified account:
| Request | Allowed when | Otherwise |
|---|---|---|
POST /fundraisers | The body sends status: "draft" explicitly, and the account holds fewer than 5 live drafts | 403 EMAIL_NOT_VERIFIED, or 403 UNVERIFIED_DRAFT_LIMIT at the cap |
PATCH /fundraisers/:id | The campaign is currently draft, and status is either omitted or "draft" | 403 EMAIL_NOT_VERIFIED |
Anything that publishes (active), submits (pending) or changes another status | Never | 403 EMAIL_NOT_VERIFIED |
| Editing a campaign that is no longer a draft | Never | 403 EMAIL_NOT_VERIFIED |
An omitted status is not a draft
On create, a body without status is treated as a publish attempt by the gates: the publish checks run as if you had sent active. For an unverified account that is a 403, so always send status: "draft" when saving work in progress.
PATCH /fundraisers/:id is the only route that moves a campaign out of draft — there is no separate publish or submit endpoint — so these two gates are the whole perimeter. Ownership is checked as usual, and an unknown campaign id answers 404.
Cover and gallery uploads (POST /storage/upload, POST /images/save-from-url) were never email-gated, so a draft can carry its photos already.
The draft cap
An unverified account may hold at most 5 drafts at once (live rows with status = 'draft', not soft-deleted). The sixth create is refused:
{
"error": "Draft limit reached",
"message": "You can keep up to 5 drafts until you verify your email address. Verify your email, or delete a draft, to save another.",
"code": "UNVERIFIED_DRAFT_LIMIT",
"action": "verify_email",
"limit": 5
}The cap is a bound on abuse, not a quota: it is counted before the insert without a lock, so two creates racing at four drafts can both land. Deleting a draft (DELETE /fundraisers/:id) frees a slot. Verifying the email removes the cap.
The refusal body
Every email-gate refusal, on these routes and on every other verified-email route, has the same body, so one client handler covers them all:
{
"error": "Email verification required",
"message": "Please verify your email address to perform this action.",
"code": "EMAIL_NOT_VERIFIED",
"action": "verify_email"
}Key off code, not error. When action is verify_email, offer to resend the verification mail with POST /cognito/resend-verification ({ "email": "…" }, limited to 5 a minute) — see Authentication.
Example: save a draft while unverified
curl -X POST https://api.fundlyhub.org/api/v1/fundraisers \
-H "Authorization: Bearer $FUNDLYHUB_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"title": "Help the Riverside Library",
"slug": "help-the-riverside-library",
"goal_amount_cents": 500000,
"status": "draft"
}'Later edits to the same draft work the same way, as long as they do not send a non-draft status:
curl -X PATCH https://api.fundlyhub.org/api/v1/fundraisers/$ID \
-H "Authorization: Bearer $FUNDLYHUB_API_KEY" \
-H 'Content-Type: application/json' \
-d '{ "summary": "New shelves and a reading corner for kids" }'After the email is verified, publish with PATCH and status: "active"; the normal publish gate applies.
Related
- Fundraisers API — the full create and update contract
- Save a draft before verifying your email — the user guide
- Reference:
POST /fundraisers·PATCH /fundraisers/{id}