upcoach API endpoint reference

upcoach API endpoint reference

Every endpoint of the upcoach API, grouped by what it works on. Paths are relative to the base URL, https://v3.upcoach.com/s/api/v1, and every request sends Authorization: Bearer <your token>. The basics (errors, pages, rate limits) are in Use the upcoach API and webhooks.

The examples are real responses from a demo workspace, shortened to one or two items.

EndpointWhat it doesScope
GET /meThe token’s person and workspaceany
GET /programsList programsapi:read
GET /programs/{id}One programapi:read
GET /programs/{id}/membersList a program’s membersapi:read
POST /programs/{id}/invitationsInvite people to a programapi:write
DELETE /programs/{id}/members/{user_id}Remove someone from a programapi:write
GET /membersList the workspace’s peopleapi:read
GET /eventsList events across the workspaceapi:read
GET /programs/{id}/eventsList a program’s eventsapi:read
GET /completionsList activity completionsapi:read
GET /enrollmentsList offer enrollmentsapi:read
GET /webhooks, POST /webhooks, GET /webhooks/{id}, DELETE /webhooks/{id}, GET /webhooks/samples/{event}Webhook subscriptionswebhooks:manage

Your token

GET /me

The token’s person, its workspace and the scopes it holds. Use it to check a token works.

{
  "data": {
    "user": { "id": "4704aa8c-30dd-4cf6-9b3c-0104bf275f44", "name": "Maya Chen", "email": "[email protected]" },
    "workspace": { "id": "org-1b547af4-c0cb-4f48-8d4e-d7a1fef601b2", "name": "Brightpath Coaching", "url": "https://brightpath.upcoach.com/" },
    "scopes": ["api:read", "api:write", "webhooks:manage"]
  }
}

Programs

GET /programs

Your programs, newest first.

ParameterWhat it does
searchPart of the program’s name, in any letter case
statuspublished or draft
limit, cursorPage size and the next page
curl "https://v3.upcoach.com/s/api/v1/programs?limit=2" \
  -H "Authorization: Bearer <your token>"
{
  "data": [
    {
      "id": "ef6e8b2e-7e21-4b84-af23-453203c0444e",
      "name": "12-Week Leadership Program",
      "url": "https://brightpath.upcoach.com/s/spaces/coaching-cohort",
      "status": "draft",
      "type": null,
      "starts_at": null,
      "member_count": 2,
      "created_at": "2026-11-11T05:52:28Z"
    },
    {
      "id": "97173975-aa90-4c20-b5ad-83cb7d4b94ae",
      "name": "Wellness Program",
      "url": "https://brightpath.upcoach.com/s/spaces/wellness-program",
      "status": "published",
      "type": null,
      "starts_at": null,
      "member_count": 2,
      "created_at": "2026-11-11T05:52:28Z"
    }
  ],
  "next_cursor": null
}

type is the program type it was made from, if any. member_count counts active participants. url opens the program; its address contains /spaces/, an older name for programs.

GET /programs/{id}

One program, with the same fields, as { "data": {...} }. A program of another workspace is not found (404).

Members and invitations

GET /programs/{id}/members

A program’s members, newest membership first.

ParameterWhat it does
roleparticipant, coach, program_admin, program_sponsor or reporting_sponsor
searchPart of the name or email
emailAn exact email address, in any letter case
limit, cursorPage size and the next page
{
  "data": [
    {
      "id": "7770c4ed-c615-4cab-a103-3de4cc7958d7",
      "name": "Priya Nair",
      "email": "[email protected]",
      "timezone": "UTC",
      "role": "participant",
      "status": "pending",
      "joined_at": "2026-11-11T05:52:28Z"
    }
  ],
  "next_cursor": "eyJpdiI6IkcrMHBmYzc0MHVRYk5Ndkd1TE45UUE9PSIs..."
}

status is active, pending (invited but never joined) or deactivated. joined_at is when they were added to the program. coach is the program’s Program Facilitator.

POST /programs/{id}/invitations

Emails each address the same invite the Users page sends, as a participant or a coach.

  • Someone already in your workspace is in the program at once (in_program: true). Anyone else joins it when they accept.
  • Someone already in the program is left out, under not_invited with a reason. If they never joined, their invite is still pending and you invite them with the same role, they get it again (invite: "resent", or "new" if the old invite had expired).
  • Participants count toward your plan’s client limit and coaches toward its admin seats (seats and billing). A batch that would go over is refused with a 409. If some invites went out before the limit was reached, message names them.
  • Invite emails count toward the workspace’s invite email caps. Over them, you get a 429.
FieldWhat it is
emails1 to 20 addresses, each listed once
roleparticipant or coach
curl -X POST "https://v3.upcoach.com/s/api/v1/programs/ef6e8b2e-7e21-4b84-af23-453203c0444e/invitations" \
  -H "Authorization: Bearer <your token>" \
  -H "Content-Type: application/json" \
  -d '{"emails":["[email protected]","[email protected]"],"role":"participant"}'
{
  "data": {
    "program": { "id": "ef6e8b2e-7e21-4b84-af23-453203c0444e", "name": "12-Week Leadership Program", "url": "https://brightpath.upcoach.com/s/spaces/coaching-cohort" },
    "role": "participant",
    "invited": [
      { "email": "[email protected]", "status": "invited", "invite": "new", "in_program": false },
      { "email": "[email protected]", "status": "invited", "invite": "new", "in_program": false }
    ],
    "not_invited": []
  }
}

Inviting Sam again, this time as a coach:

{
  "data": {
    "program": { "id": "ef6e8b2e-7e21-4b84-af23-453203c0444e", "name": "12-Week Leadership Program", "url": "https://brightpath.upcoach.com/s/spaces/coaching-cohort" },
    "role": "coach",
    "invited": [],
    "not_invited": [
      {
        "email": "[email protected]",
        "status": "not_invited",
        "reason": "other_role",
        "message": "Already in this program as Participant; nothing was sent and their role is unchanged."
      }
    ]
  }
}

reason is already_member (in the program with this role), other_role (in it with another role, which stays) or cannot_invite (an address that can’t be invited).

DELETE /programs/{id}/members/{user_id}

Removes the member from the program and notifies them. Their account and their place in the workspace stay. Owners and Admins of the workspace, and a program’s only Program Admin, can only be removed on the program’s Members page (403). Someone who isn’t in the program is not found (404).

curl -X DELETE "https://v3.upcoach.com/s/api/v1/programs/ef6e8b2e-7e21-4b84-af23-453203c0444e/members/6548f98c-5eb0-458c-80e9-441bf5116cae" \
  -H "Authorization: Bearer <your token>"
{ "data": { "program_id": "ef6e8b2e-7e21-4b84-af23-453203c0444e", "member_id": "6548f98c-5eb0-458c-80e9-441bf5116cae", "removed": true } }

GET /members

Everyone in your workspace, deactivated people included, newest first, as Admin Area > Users lists them. Filter with email (an exact address).

{
  "data": [
    {
      "id": "698fffed-b97e-4711-808d-d3cce8817363",
      "name": "Sam Rivera",
      "email": "[email protected]",
      "timezone": "UTC",
      "org_role": null,
      "status": "pending",
      "joined_at": null
    }
  ],
  "next_cursor": null
}

org_role is owner, admin, implementation_admin or null. joined_at is when they joined the workspace.

Events

GET /events and GET /programs/{id}/events

Events that start between from and to, earliest first: across the workspace (the ones the token’s person may see), or in one program.

ParameterWhat it does
fromAn ISO 8601 date or date-time, UTC unless it carries an offset. Defaults to the start of today (UTC)
toDefaults to 30 days after from, at most 366 days after it
limit, cursorPage size and the next page
curl "https://v3.upcoach.com/s/api/v1/programs/97173975-aa90-4c20-b5ad-83cb7d4b94ae/events?from=2026-11-12&to=2026-12-31" \
  -H "Authorization: Bearer <your token>"
{
  "data": [
    {
      "id": "b238aee2-9243-4457-955f-eb42a7ad347c",
      "title": "Group coaching call",
      "starts_at": "2026-11-18T14:00:00Z",
      "ends_at": "2026-11-18T15:00:00Z",
      "timezone": "UTC",
      "duration_minutes": 60,
      "status": "upcoming",
      "location_type": "link",
      "location_detail": "https://example.com/meet",
      "series_id": null,
      "is_one_to_one_session": false,
      "hosts": [
        { "id": "38289adf-a43e-4d34-bf11-7034afea768b", "name": "Jordan Lee", "email": "[email protected]", "timezone": "UTC" }
      ],
      "program": { "id": "97173975-aa90-4c20-b5ad-83cb7d4b94ae", "name": "Wellness Program", "url": "https://brightpath.upcoach.com/s/spaces/wellness-program" },
      "going_count": 0
    }
  ],
  "next_cursor": null
}

status is upcoming, in_progress or past. location_type is link, in_person or null, and location_detail the meeting link or address. Events of one recurring series share a series_id. going_count counts active members who said they’re going.

Completions

GET /completions

Activities members completed, newest first. Filter with program_id and since (an ISO 8601 time). Each item has the same fields as the activity.completed webhook, plus an id that is the same every time for one person and one activity.

{
  "data": [
    {
      "id": "e81ff51d-1bc7-5595-bec1-507b2b1bccc2",
      "member": { "id": "698fffed-b97e-4711-808d-d3cce8817363", "name": "Sam Rivera", "email": "[email protected]", "timezone": "UTC" },
      "activity": { "id": "10172d34-76eb-4c46-b99f-277d5375872f", "title": "Reflection Worksheet", "type": "worksheet" },
      "module": { "id": "8a05598d-ed54-41cd-a89d-f76264556eb4", "title": "Module 1" },
      "section": { "id": "1de23536-c393-47a1-af84-d849c20ae39c", "title": "Foundation Week" },
      "program": { "id": "97173975-aa90-4c20-b5ad-83cb7d4b94ae", "name": "Wellness Program", "url": "https://brightpath.upcoach.com/s/spaces/wellness-program" },
      "completed_at": "2026-11-11T05:55:11Z"
    }
  ],
  "next_cursor": null
}

Enrollments

GET /enrollments

Offer purchases and free joins, newest first, as { "data": [...], "next_cursor": ... }. Filter with offer_id. Each item has the enrollment’s id plus the same fields as the enrollment.created webhook. Those fields, as in the data of the example GET /webhooks/samples/enrollment.created returns:

{
  "member": { "id": "0b9d2f4e-1c3a-4e5b-8d7f-6a2c9e1b3d40", "name": "Sam Rivera", "email": "[email protected]", "timezone": "Europe/London" },
  "offer": { "id": "8d2f6b4a-1e3c-4a7d-9b5f-2c0e7a1d4b36", "name": "Leadership Program" },
  "programs": [
    { "id": "7c4e1a9b-2d3f-4b6a-8e5c-1f0d9a7b3c21", "name": "12-Week Leadership Program", "url": "https://brightpath.upcoach.com/s/spaces/12-week-leadership-program" }
  ],
  "amount": 49900,
  "currency": "usd",
  "coupon": null,
  "is_free": false,
  "enrolled_at": "2026-11-12T09:30:00Z"
}

amount is in the currency’s smallest unit (cents), and null for a free offer. coupon has the coupon’s code and name when one was used.

Webhooks

Subscribe a URL to events, list and delete subscriptions, and get example payloads. The events, payloads and signature are in Send events to your tools with webhooks.

POST /webhooks

FieldWhat it is
urlAn https URL on port 443 that resolves to public internet addresses only
eventsOne or more event types, such as program.member_joined
program_idOptional: send only this program’s events
curl -X POST "https://v3.upcoach.com/s/api/v1/webhooks" \
  -H "Authorization: Bearer <your token>" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://hooks.example.com/upcoach","events":["program.member_joined","activity.completed"]}'

The answer is a 201 with the subscription and its signing secret. The secret is shown only here:

{
  "data": {
    "id": "4333cd09-bc03-4ec3-908e-17caea2cb6c7",
    "url": "https://hooks.example.com/upcoach",
    "events": ["program.member_joined", "activity.completed"],
    "program_id": null,
    "status": "active",
    "disabled_reason": null,
    "created_at": "2026-11-11T05:55:04Z",
    "secret": "whsec_…"
  }
}

A URL that isn’t public is refused:

{ "error": { "code": "validation_failed", "message": "The request is not valid.", "fields": { "url": ["Webhooks can only be sent to public internet addresses."] } } }

Creating subscriptions counts toward the rate limits.

GET /webhooks and GET /webhooks/{id}

The workspace’s subscriptions, newest first, whoever made them, on the Webhooks page or over the API. The same fields, without the secret. status is active or disabled, and disabled_reason says why: failing or access_revoked.

DELETE /webhooks/{id}

Deletes the subscription and its delivery log. Answers 204 with no body.

GET /webhooks/samples/{event}

Up to 3 payloads of the event that your workspace sent in the last 7 days, exactly as delivered, newest first. With none yet, one fixed example.

What to do next