This page covers changing a membership after it starts. Two parties can act on it: the holder, with their own token, and the organization, with an org-scoped token. They use different paths and have different powers.
All paths are under https://api.davi.social/api/v1.
Check access with the dates, not the status
status is active, cancelled, expired or revoked. It records how a
membership ended, not whether it is running. Two nullable dates answer that:
| Field | Set by | Means |
|---|---|---|
valid_until | the term | When access ends. null never expires |
cancelled_at | the holder | The holder gave notice. The membership will not continue past valid_until |
status lags in both directions:
- A membership under notice stays
activeuntil it lapses.cancelled_atis the notice;statusis the outcome. - A membership whose term has just run out still reads
activeuntil a sweep reconciles it shortly afterwards: tocancelledif notice was given, otherwise toexpired.
Gate access on valid_until, not on status == "active". Gating on the status
lets a lapsed membership through until the sweep runs, and locks out a holder
who cancelled but has paid time left.
Notice does not withdraw anything. The tier, the membership entitlements and
the membership wallet all stay in place until valid_until.
As the holder
These act on the caller's own membership with the caller's own token. There is
no user_uuid in the path; the token identifies the holder.
| Method | Path | Scope |
|---|---|---|
GET | /memberships | user:read |
GET | /memberships/{organization_slug}/tiers | membership:read |
GET | /memberships/{organization_slug}/history | membership:read |
GET | /memberships/{organization_slug}/cards | membership:read |
PATCH | /memberships/{organization_slug} | membership:tier:update |
POST | /memberships/{organization_slug}/renew | membership:renew |
POST | /memberships/{organization_slug}/resume | membership:renew |
DELETE | /memberships/{organization_slug} | membership:leave |
A caller with no membership in the organization and an organization that does
not exist both answer 404.
Cancel, and resume
curl -X DELETE \
"https://api.davi.social/api/v1/memberships/ORG_SLUG" \
-H "Authorization: Bearer ACCESS_TOKEN"
The response carries cancelled_at and the valid_until on which access ends.
Two kinds of membership end immediately, with valid_until set to now: a
one-time membership, which never had an expiry, and one that had already
lapsed.
POST /memberships/{organization_slug}/resume withdraws the notice and changes
nothing else. It works only on a membership that is still running. A lapsed
membership cannot be resumed; the holder joins again instead.
Cancelling twice, or resuming a membership that was never cancelled, answers
409.
Renew
curl -X POST \
"https://api.davi.social/api/v1/memberships/ORG_SLUG/renew" \
-H "Authorization: Bearer ACCESS_TOKEN"
The new term is added to the end of the current one, not to the moment of renewal, so renewing early keeps the time left. Renewing also withdraws a cancellation.
A membership stays renewable for two weeks after valid_until, until
renewable_until. Renewing inside that window continues the same membership
and history. After it, the membership is retired, and coming back is joining
again on a fresh term, with the old history left behind.
Only a free membership can be renewed with this call. A membership with a
price, whether the tier's or a custom price set for that holder, answers 402
with the amount and currency owed. Send the holder through checkout instead;
the new term follows the settled payment.
Move to another tier
Send PATCH /memberships/{organization_slug} with a tier the organization
offers openly. The remaining term carries over, converted into the new tier's
days by value: it buys fewer days on a more expensive tier and more on a
cheaper one. No money moves. The join date, history and membership stay the
same.
A tier the organization keeps closed, a tier belonging to another organization,
and a tier that does not exist all answer 404.
As the organization
These need an org-scoped token and name the holder
by user_uuid.
| Method | Path | Scope |
|---|---|---|
POST | /organizations/{organization_slug}/memberships | membership:join |
PATCH | /organizations/{organization_slug}/memberships/{user_uuid} | membership:tier:update |
POST | /organizations/{organization_slug}/memberships/{user_uuid}/renew | membership:renew |
DELETE | /organizations/{organization_slug}/memberships/{user_uuid} | membership:leave |
DELETE | /organizations/{organization_slug}/memberships/{user_uuid}/revocation | membership:join |
POST | /organizations/{organization_slug}/memberships/{user_uuid}/restore | membership:join |
End a membership
DELETE /organizations/{organization_slug}/memberships/{user_uuid} gives a
week's notice. The membership keeps granting everything it granted until the
week runs out, then it is retired. Pass revoke_immediately to skip the
notice.
To call it off within the week, send
DELETE /organizations/{organization_slug}/memberships/{user_uuid}/revocation.
Nothing was taken away, so nothing needs to be put back.
A pending revocation is visible to the organization only. The organization's
membership list carries revoke_at. The holder's GET /memberships has no
such field, and no email or event is sent. On a holder-facing page, the only
ending you can show is a cancellation the holder made themselves.
Restore an ended membership
After the notice has run out, revocation no longer applies. Use
POST /organizations/{organization_slug}/memberships/{user_uuid}/restore
instead. It brings back the most recently ended membership as it stood: the
term resumes with the time it had left rather than restarting, and a term that
expired in the meantime comes back expired.
- Notice the holder had given stays in place. Withdrawing it is the holder's action.
- If the holder has rejoined in the meantime, the restore is refused. An organization cannot have two live memberships for the same person.
Enrolling the person again instead starts a term from today, and the history shows them leaving and coming back.
Read the history
GET /memberships/{organization_slug}/history for the holder, or
GET /organizations/{organization_slug}/memberships/{user_uuid}/history for
the organization. Entries are newest first and cover earlier memberships too:
a membership that lapsed and was later rejoined is a separate record. Each
entry names the tier it was on, so tier moves appear in sequence.
A pending revocation does not appear in the history, because the membership is still live.
Next
- Membership Tiers and Members: tiers, and enrolling users.
- Feature Gating: the membership entitlements a tier grants.