Skip to content

Roles & Permissions ​

FundlyHub uses role-based access control. Roles are assigned to users, permissions are assigned to roles, and protected endpoints name the permission they require — never the role.

A protected endpoint answers:

  1. 401 when the request carries no valid credential (a session cookie, a Cognito ID token or an API key — see Authentication).
  2. 403 when the caller is signed in but their effective permission set for the request's scope does not contain the permission. The body names what was missing:
json
{
  "error": "Forbidden",
  "message": "Requires permission: endorse_campaigns",
  "required_permission": "endorse_campaigns"
}

GET /permissions/check has been removed ​

Removed endpoint

GET /permissions/check no longer exists, and any request to it returns 404. Use GET /me/capabilities, which answers the same question for the calling user.

GET /me/capabilities ​

Returns the authenticated caller's own effective permissions and roles. This is the correct way for a client to decide what to show or attempt.

Auth: signed in. No permission is required — every authenticated caller may read their own capabilities.

Query parameters ​

ParameterRequiredDescription
scopeTypeNoOne of global, organization, fundraiser. Defaults to global. Any other value returns 400.
scopeIdConditionalThe organization or fundraiser UUID. Required whenever scopeType is not global; omitting it returns 400.

Example ​

bash
curl -b cookies.txt \
  "https://api.fundlyhub.org/api/v1/me/capabilities?scopeType=global"
json
{
  "permissions": ["create_fundraiser", "edit_own_fundraiser"],
  "roles": ["creator"],
  "scope": { "type": "global", "id": null },
  "fetchedAt": "2026-09-04T12:00:00.000Z",
  "expiresAt": "2026-09-04T12:05:00.000Z"
}

permissions is the flat list of effective permission names. roles is the list of role names held in that scope. Global assignments always apply, so a global role shows up in an organization-scoped or fundraiser-scoped response too.

expiresAt is a cache hint, not a session deadline

Permission sets are cached briefly. expiresAt tells a client when its copy should be considered stale. It says nothing about how long the caller's session is valid.

Roles a client will see ​

RoleScopeNotes
userGlobalEvery account
donorGlobalPlatform donor
creatorGlobalFundraiser creator; granted, for example, when a first creator tier is created
verified_creatorGlobalVerified fundraiser creator
ambassadorGlobalReferral partner: view_own_referral_portal and endorse_campaigns — see Ambassadors
org_ownerOrganizationAuto-granted on organization registration
org_adminOrganizationManage org members, settings and payouts
org_viewerOrganizationRead-only access to an organization's admin panel

FundlyHub's own staff roles also exist; they are not part of this public API. The organization roles and their org.* permissions are described on Organization Admin.

Roles do not inherit each other's permissions

A role holds exactly the permissions mapped to it — not the union of everything held by roles below it. Always check for the permission an action needs (from GET /me/capabilities), never for a role name.

Scope ​

A permission is checked in the scope of the request: an organization's routes check the organization scope, a campaign's routes may check the fundraiser scope, and everything else is global. Global assignments apply in every scope; a scoped assignment applies only in its own scope.

Error responses ​

CodeMeaning
400Validation error — for example an invalid scopeType or a missing scopeId on /me/capabilities
401No authentication token was presented
403Authenticated, but missing the required permission — see required_permission
404Not found

Built with VitePress