FriendChise Docs

Users and Accounts

Endpoints for the current user profile, organization membership list, and account deletion

Get current user

GET /api/mobile/me

Returns the profile of the currently authenticated user. Used by the mobile app after sign-in to confirm identity and load the user's name and avatar.

Authentication

Bearer token required.

Response

{
  "user": {
    "id": "usr_01abc",
    "name": "Alex Chen",
    "email": "alex.chen@example.com",
    "image": "https://lh3.googleusercontent.com/..."
  }
}

Errors

StatusReason
401Token missing or invalid
404Authenticated user ID has no database record

List user organizations (mobile)

GET /api/mobile/me/organizations

Returns all organizations the authenticated user is a member of, ordered by name. Used by the mobile app to populate the org picker.

Authentication

Bearer token required.

Response

{
  "organizations": [
    {
      "id": "org_01abc",
      "name": "Downtown Doughnuts",
      "image": "https://cdn.friendchise.app/orgs/..."
    }
  ]
}

image is a resolved public URL, or null if the org has no image set.

Errors

StatusReason
401Token missing or invalid

Invite inbox (mobile)

GET /api/mobile/me/invites

Returns the authenticated user's invite inbox, paged for normal pagination or infinite scroll. Each invite includes a subtype field so the client can tell member, bot-slot, and franchise invites apart.

Authentication

Bearer token required.

Query parameters

ParamTypeDefaultMaxDescription
pageinteger1—Page number
pageSizeinteger2050Items per page
limitinteger2050Alias for pageSize
viewall | unseenall—Filter to unseen invites only
searchstring——Search by org name, inviter name, or invite metadata

Response

{
  "invites": [
    {
      "id": "inv_01abc",
      "type": "MEMBER",
      "subtype": "MEMBER",
      "status": "PENDING",
      "orgId": "org_01abc",
      "orgName": "Downtown Doughnuts",
      "inviterName": "Alex Chen",
      "seenAt": null,
      "expiresAt": null,
      "createdAt": "2026-01-15T09:00:00.000Z",
      "acceptedAt": null,
      "declinedAt": null,
      "metadata": {}
    }
  ],
  "total": 1,
  "totalPages": 1,
  "page": 1,
  "pageSize": 20,
  "hasMore": false
}

POST /api/mobile/me/invites/[inviteId]/accept

Accepts a pending member invite or bot-slot invite. Franchise invites are accepted through the franchise join flow using their token.

Authentication

Bearer token required.

Response

Returns { "ok": true } when the invite is accepted. Franchise invites return the newly created organization payload as organization.

Errors

StatusReason
401Token missing or invalid
404Invite not found

| 400 | Invite expired or invalid for this accept flow | | 409 | Invite was already handled or a conflict occurred while accepting |


POST /api/mobile/me/invites/[inviteId]/decline

Declines a pending member, bot-slot, or franchise invite.

Authentication

Bearer token required.

Response

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

Errors

StatusReason
401Token missing or invalid
404Invite not found or already handled
409Invite is no longer pending

Create organization (mobile)

POST /api/mobile/me/organizations

Creates a standalone organization for the authenticated mobile user.

Authentication

Bearer token required.

Request body

Same shape as the web create-org endpoint:

{
  "title": "Downtown Doughnuts",
  "timezone": "Australia/Sydney",
  "address": "42 Harbour Street",
  "operatingDays": ["mon", "tue", "wed"],
  "openTimeMin": 360,
  "closeTimeMin": 1080
}

Response

Returns the created organization payload, including a public image URL when present.

Errors

StatusReason
401Token missing or invalid
400Validation failed
400Malformed JSON body.
429Demo organization limit reached

Join organization (mobile)

POST /api/mobile/me/organizations/join

Joins an existing franchise using a one-time invite token.

Authentication

Bearer token required.

Request body

{
  "token": "franchise-invite-token"
}

Schedule fields are optional and use the same validation as the web join flow.

Response

Returns the newly created franchise org payload, including a public image URL when present.

Errors

StatusReason
401Token missing or invalid
400Validation failed or the invite token was rejected
400Malformed JSON body.

Leave organization (mobile)

DELETE /api/mobile/me/organizations/[orgId]/leave

Leaves the current organization by converting the caller's membership to a bot placeholder.

Authentication

Bearer token required.

Response

Returns { "ok": true } when the caller leaves the organization.

Errors

StatusReason
401Token missing or invalid
404Membership not found
403Organization owners can't leave. Transfer ownership first.
400Membership is already a bot

Delete organization (mobile)

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

Deletes a mobile organization when the caller provides the exact organization name as confirmation.

Authentication

Bearer token required.

Request body

{
  "confirmName": "Downtown Doughnuts"
}

confirmName must match the organization name exactly.

Response

Returns { "ok": true } when the organization is deleted.

Errors

StatusReason
401Token missing or invalid
400Validation failed
400Confirmation name does not match
400Malformed JSON body.
415Unsupported media type.
403Caller is not the owner or org is franchise-scoped
404Organization not found

Get default organization (mobile)

GET /api/mobile/me/organization

Returns the user's first organization by name. Used by the mobile app when no explicit org has been selected yet.

Authentication

Bearer token required.

Response

{
  "organization": {
    "id": "org_01abc",
    "name": "Downtown Doughnuts"
  },
  "orgId": "org_01abc"
}

Errors

StatusReason
401Token missing or invalid
404User has no org memberships

List user organizations (web)

GET /api/me/organizations

Returns a paginated list of organizations for the current web session user. Supports search and active-org resolution.

Authentication

Session cookie (web only).

Query parameters

ParamTypeDefaultMaxDescription
pageinteger1—Page number
limitinteger24100Items per page
searchstring——Filter orgs by name
activeOrgIdstring——Org ID to resolve alongside the paginated list

Response

{
  "organizations": [
    { "id": "org_01abc", "name": "Downtown Doughnuts", "image": null }
  ],
  "activeOrganization": {
    "id": "org_01abc",
    "name": "Downtown Doughnuts",
    "image": null
  },
  "totalCount": 3,
  "totalPages": 1,
  "page": 1,
  "pageSize": 24,
  "search": ""
}

If no session is present, returns an empty result (totalCount: 0) rather than a 401.


Create organization (web)

POST /api/me/organizations

Creates a new standalone organization owned by the authenticated user.

Authentication

Session cookie (web only).

Request body

{
  "title": "Downtown Doughnuts",
  "timezone": "Australia/Sydney",
  "address": "123 Main St",
  "operatingDays": ["mon", "tue", "wed", "thu", "fri"],
  "openTimeMin": 480,
  "closeTimeMin": 1020
}

title is required (max 100 characters). All other fields are optional.

Response

201 Created

{
  "organization": {
    "id": "org_01abc",
    "name": "Downtown Doughnuts",
    "timezone": "Australia/Sydney",
    "address": "123 Main St",
    "operatingDays": ["mon", "tue", "wed", "thu", "fri"],
    "openTimeMin": 480,
    "closeTimeMin": 1020,
    "image": null
  }
}

Errors

StatusReason
401Session missing or invalid
400Malformed JSON body or validation failed
429Demo limit reached

Delete account

DELETE /api/account/delete

Permanently deletes the authenticated user's account after confirmation.

Authentication

Bearer token or session cookie.

Request body

{
  "confirmText": "Alex Chen"
}

confirmText must exactly match the user's display name if one exists, otherwise their email address. The comparison is case-sensitive and is only used as the deletion confirmation check.

Response

{ "ok": true }

Errors

StatusReason
400confirmText is missing from the request body
400confirmText does not match the user's name or email
403Session-cookie request did not come from the same origin
401Not authenticated
404User record not found
500Unexpected error during deletion

Important notes

  • Deletion is permanent and irreversible.
  • Any organizations the user owns remain in place, but their ownerId is cleared before the account row is deleted.
  • The user's memberships are converted to bot accounts before deletion.
  • Session-cookie requests must come from the same origin; bearer-token requests are allowed without the browser-origin check.