FriendChise Docs

Organizations

Endpoints for memberships, announcements, roster, and org ownership checks

All organization endpoints are scoped to orgId. The caller must be a member or have a relevant permission.

Mobile memberships

GET /api/mobile/me/organizations/[orgId]/memberships

Returns a paged list of organization members for the authenticated mobile user. The response includes page, pageSize, totalCount, totalPages, and hasMore so the client can use either classic pagination or infinite scroll.

Authentication

Bearer token required.

Query parameters

ParamTypeDefaultMaxDescription
pageinteger1—Page number
pageSizeinteger2050Items per page
searchstring——Search by member name/email/bot name
roleIdstring——Filter to members with a specific role
excludeIdsstring[]——Member IDs to exclude
excludeBotsbooleanfalse—Exclude bot memberships

Response

{
  "memberships": [
    {
      "id": "mem_01abc",
      "userId": "usr_01abc",
      "botName": null,
      "status": "ACTIVE",
      "joinedAt": "2026-01-15T09:00:00.000Z",
      "workingDays": ["mon", "tue"],
      "user": {
        "id": "usr_01abc",
        "name": "Alex Chen",
        "email": "alex@example.com",
        "image": null
      },
      "memberRoles": [
        { "role": { "id": "role_01", "name": "Baker", "color": "#f59e0b" } }
      ],
      "name": "Alex Chen",
      "description": "alex@example.com",
      "image": null
    }
  ],
  "totalCount": 8,
  "totalPages": 1,
  "page": 1,
  "pageSize": 20,
  "hasMore": false
}

GET /api/mobile/me/organizations/[orgId]/roles

Returns a paged list of roles for the organization, ordered with default roles first. Use this for role-pickers and infinite scroll UIs.

Authentication

Bearer token required.

Query parameters

ParamTypeDefaultMaxDescription
pageinteger1—Page number
pageSizeinteger2050Items per page
limitinteger2050Alias for pageSize
searchstring——Filter by role name

Response

{
  "roles": [
    {
      "id": "role_01",
      "name": "Owner",
      "color": "#ef4444",
      "isDefault": false
    }
  ],
  "totalCount": 6,
  "totalPages": 1,
  "page": 1,
  "pageSize": 20,
  "hasMore": false
}

Use the membership update endpoint to assign roles to a person:

PATCH /api/mobile/me/organizations/[orgId]/memberships/[membershipId]

Body:

{ "roleIds": ["role_01", "role_02"] }

POST /api/mobile/me/organizations/[orgId]/memberships

Invites a member by email.

Authentication

Bearer token required and MANAGE_MEMBERS permission required.

Request body

{
  "email": "new.member@example.com",
  "roleIds": ["role_01"],
  "workingDays": ["mon", "tue"]
}

Response

Returns { "ok": true } when the invite is created.

Errors

StatusReason
400Validation failed or email/user/role data is invalid
401Token missing or invalid
403Missing MANAGE_MEMBERS permission
409The user is already a member or already has a pending invite

DELETE /api/mobile/me/organizations/[orgId]/memberships/[membershipId]

Removes a member from the organization.

Authentication

Bearer token required and MANAGE_MEMBERS permission required.

Response

Returns { "ok": true } when the member is removed.

Errors

StatusReason
401Token missing or invalid
403Missing MANAGE_MEMBERS permission, or the member is the organization owner
404Membership not found

POST /api/mobile/me/organizations/[orgId]/memberships/[membershipId]/convert

Converts a member to a bot, or a bot to a member.

Authentication

Bearer token required and MANAGE_MEMBERS permission required.

Request body

{ "kind": "bot" }

or

{ "kind": "member", "userId": "usr_01abc" }

Response

Returns { "ok": true } when the conversion succeeds.

Errors

StatusReason
401Token missing or invalid
403Missing MANAGE_MEMBERS permission, or the owner cannot be converted to a bot
404Membership not found or the target user was not found
409The target user already has a membership in the org
400Validation failed or the membership is already in the requested state

List memberships

GET /api/orgs/[orgId]/memberships

Returns a paginated list of members in the organization. Supports filtering by role, search, exclusion lists, and bot exclusion.

Authentication

Requires org membership.

Query parameters

ParamTypeDefaultMaxDescription
pageinteger1—Page number
pageSizeinteger1050Items per page
searchstring——Filter by user name or email
roleIdstring——Filter to members with a specific role
excludeIdsstring[]——Member IDs to exclude (repeatable: ?excludeIds=a&excludeIds=b)
excludeBotsbooleanfalse—When true, exclude bot/system members

Response

{
  "memberships": [
    {
      "id": "mem_01abc",
      "userId": "usr_01abc",
      "botName": null,
      "status": "ACTIVE",
      "joinedAt": "2026-01-15T09:00:00.000Z",
      "workingDays": ["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY"],
      "user": {
        "id": "usr_01abc",
        "name": "Alex Chen",
        "email": "alex@example.com",
        "image": null
      },
      "memberRoles": [
        { "role": { "id": "role_01", "name": "Baker", "color": "#f59e0b" } }
      ],
      "name": "Alex Chen",
      "description": "alex@example.com"
    }
  ],
  "totalCount": 8,
  "totalPages": 1,
  "page": 1,
  "pageSize": 10,
  "hasMore": false
}

List announcements

GET /api/orgs/[orgId]/announcements

Returns paginated announcements for the organization, ordered by newest or oldest.

Authentication

Requires org membership.

Query parameters

ParamTypeDefaultMaxDescription
pageinteger1—Page number
pageSizeinteger1050Items per page
ordernewest | oldestnewest—Sort direction

Response

{
  "announcements": [
    {
      "id": "ann_01abc",
      "title": "New opening hours for the holiday weekend",
      "body": "...",
      "createdAt": "2026-08-10T12:00:00.000Z"
    }
  ],
  "page": 1,
  "totalPages": 2,
  "pageSize": 10,
  "order": "newest"
}

Get roster entries

GET /api/orgs/[orgId]/roster-entries

Returns roster entries for one or more week-start dates. Used by the roster page to load the visible schedule window incrementally.

Authentication

Requires org membership.

Query parameters

ParamTypeRequiredDescription
weeksstringYesComma-separated ISO 8601 week-start dates (e.g. 2026-08-10,2026-08-17). Maximum 20 weeks per request.

Example request

GET /api/orgs/org_01abc/roster-entries?weeks=2026-08-10,2026-08-17

Response

Array of roster entry objects for the requested weeks.

[
  {
    "id": "entry_01",
    "orgId": "org_01abc",
    "membershipId": "mem_01abc",
    "weekStart": "2026-08-10T00:00:00.000Z",
    "data": { ... }
  }
]

Returns an empty array if weeks is missing or none of the provided dates are valid ISO dates.


Update roster assignments

POST /api/orgs/[orgId]/roster-entries

Replaces every member assigned to a single roster cell — one (weekStart, dayIndex) pair. The members array is the complete new contents of that cell, so members left out of the request are unassigned from it, and an empty array clears the cell.

Authentication

Requires the MANAGE_MEMBERS permission, the same permission used by the roster server actions.

Body

FieldTypeRequiredDescription
weekStartstringYesISO 8601 date-time for the Monday the cell belongs to. Must match the stored week-start key exactly (00:00 UTC).
dayIndexnumberYesDay of the week, 0 (Monday) through 6 (Sunday).
membersarrayYesFull replacement set of members for the cell. Pass [] to clear it.

Each entry in members:

FieldTypeRequiredDescription
membershipIdstringYesMembership to assign. Must belong to this org.
shiftStartMinnumber | nullNoShift start in minutes from midnight. null (the default) inherits the day config.
shiftEndMinnumber | nullNoShift end in minutes from midnight. null (the default) inherits the day config.

Example request

POST /api/orgs/org_01abc/roster-entries
Content-Type: application/json

{
  "weekStart": "2026-08-10T00:00:00.000Z",
  "dayIndex": 2,
  "members": [
    { "membershipId": "mem_01abc", "shiftStartMin": 540, "shiftEndMin": 1020 },
    { "membershipId": "mem_02def", "shiftStartMin": null, "shiftEndMin": null }
  ]
}

Response

The roster entries that now occupy the cell, in the same shape returned by the GET above.

{
  "entries": [
    {
      "id": "entry_01",
      "orgId": "org_01abc",
      "membershipId": "mem_01abc",
      "weekStart": "2026-08-10T00:00:00.000Z",
      "dayIndex": 2,
      "shiftStartMin": 540,
      "shiftEndMin": 1020,
      "membership": {
        "id": "mem_01abc",
        "botName": null,
        "user": { "name": "Riley" }
      }
    }
  ]
}

Errors

StatusReason
400weekStart is missing or not a valid date, dayIndex is not an integer in 0–6, members is missing or malformed, or the JSON body could not be parsed
401Not authenticated
403Not a member of the org, or missing the MANAGE_MEMBERS permission
404One or more membershipId values do not belong to this org
415Request body was not JSON or form-encoded

Check parent owner status

GET /api/orgs/[orgId]/is-parent-owner

Checks whether the current user is the franchisor owner of this org or its parent. Used by the app shell to show or hide franchisor-only navigation.

Authentication

Requires a signed-in user (session cookie or bearer token).

Response

{
  "isParentOwner": true,
  "parentOrgId": null
}
FieldDescription
isParentOwnertrue if the current user owns the root (parent) franchise org
parentOrgIdID of the parent org if the current user owns it but is viewing a child org; null otherwise

Errors

StatusReason
401Not authenticated