Save event drafts and tags
Requires: A secret key with
MEMBER_AUTH, and the member's session.
The create-event wizard saves as the member types.
These are the routes behind that, for building or extending your own create
flow. All need a secret key with MEMBER_AUTH and the member's session.
GET /api/v1/communities/:communityTag/me/drafts
GET /api/v1/communities/:communityTag/me/drafts/event/:id
PUT /api/v1/communities/:communityTag/me/drafts/event/:id
DELETE /api/v1/communities/:communityTag/me/drafts/event/:id
GET /api/v1/communities/:communityTag/me/profile/events
GET /api/v1/communities/:communityTag/me/tags
POST /api/v1/communities/:communityTag/me/tagsDrafts
A draft is addressed by an id you mint, so the first autosave and every
later one are the same PUT:
const member = cobuntu.as(session);
const draftId = crypto.randomUUID();
await member.me.drafts.save(draftId, {
name: "Summer Meetup",
draftStep: "details",
startDate: "2026-11-20T18:00:00Z",
communityId: null, // personal; or this community's id for a community-owned draft
});name is required. A community-owned draft needs the member to hold
EVENTS_CREATE; another community's id is 403 WRONG_COMMUNITY.
Stop autosaving on 409
When the draft is created as an event, the row is no longer a draft and every
save answers 409 CONFLICT. Treat that as "done", not as an error:
try {
await member.me.drafts.save(draftId, values);
} catch (e) {
if (e instanceof CobuntuError && e.status === 409) stopAutosave();
else throw e;
}Saving on page hide
A save fired from pagehide must survive the page closing. Pass keepalive:
await member.me.drafts.save(draftId, values, { keepalive: true });List, resume, discard
const drafts = await member.me.drafts.list();
// [{ id: "drf_1", kind: "event", name: "Summer Meetup", draftStep: "details", updatedAt, communityId: null, productType: null }]
const row = await member.me.drafts.get("drf_1"); // the stored values, to refill the form
await member.me.drafts.delete("drf_1"); // 204list() returns personal drafts and this community's, never another
community's. get answers 403 FORBIDDEN for another member's draft,
404 NOT_FOUND for an unknown one and 409 CONFLICT once it became an event.
Unfinished events on the profile
me.profile.events() is the member's own Events tab: every event they host,
newest first, drafts included.
const events = await member.me.profile.events();
for (const e of events) {
if (e.isDraft) {
// "Continue setup": resume at the step they left
link(`/events/new?draft=${e.id}`, e.draftStep);
} else {
badge(e.catalogStatus); // LIVE | PENDING | HIDDEN | REJECTED | DRAFT
}
}Read isDraft, not status: status reflects listing visibility, so a
pending event reads as DRAFT there. catalogRejectionReason is set when the
community declined the listing with a reason. Drafts that belong to another
community are left out.
Tags
const hits = await member.me.tags.search("mus"); // at most 10; [] for an empty query
// [{ id: "tag_1", name: "Music", searchName: "music" }]
const tag = await member.me.tags.create("Music");create returns the existing tag when one matches case-insensitively (200),
or the new one (201). Names are up to 20 letters, digits and spaces
(400 INVALID_INPUT). Send the ids as tagIds when creating the event.