For the complete documentation index, see llms.txt. This page is also available as Markdown.

Program Enrollment API

This guide explains how to programmatically enroll Canvas users into Journey Programs using the GraphQL API.

Prerequisites

  • A Canvas API token with admin access (read_as_admin permission on the target account)

  • The Canvas instance URL for your organization

  • The Journey GraphQL endpoint URL for your environment

  • jq (for shell examples)

Authentication

Journey uses Canvas-issued JWTs for API authentication. Every request requires a Bearer token obtained from Canvas.

Obtain a Journey JWT

TOKEN=$(curl -s -X POST "https://<canvas-host>/api/v1/jwts?audience=Instructure&workflows[]=journey" \
  -H "Authorization: Bearer <canvas-api-token>" \
  | jq -r '.token' | base64 -d)

Use $TOKEN as the Authorization: Bearer header on all GraphQL requests. Tokens are short-lived — obtain a fresh one at the start of each batch job.


GraphQL Endpoint


Enrolling Users in a Program

Mutation: batchEnrollUserToProgram

Enroll users in a Program

post

Enrolls a list of Canvas users into a single Journey Program using the batchEnrollUserToProgram GraphQL mutation.

Idempotency: already-enrolled users are silently skipped and returned without error — it is safe to retry a failed batch with the same user list.

Rate limiting: the API may return a "Too Many Requests" error (HTTP 429 surfaced as a GraphQL error). Read the retryAfter value from the error extensions and wait that many seconds before retrying.

Authorizations
AuthorizationstringRequired

Journey JWT obtained from Canvas (POST /api/v1/jwts?audience=Instructure&workflows[]=journey, then base64-decode the token field).

Body
querystringRequired

GraphQL mutation string.

Example: mutation BatchEnrollUsers($programId: String!, $userIds: [String!]!, $accountId: String!) { batchEnrollUserToProgram(programId: $programId, userIds: $userIds, accountId: $accountId) { id enrollee createdAt } }
Responses
200

GraphQL always returns HTTP 200. Check the errors field in the response body to determine whether the operation succeeded.

application/json
post/graphql
POST /graphql HTTP/1.1
Host: your-journey-host.instructure.com
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 424

{
  "query": "mutation BatchEnrollUsers($programId: String!, $userIds: [String!]!, $accountId: String!) {\n  batchEnrollUserToProgram(programId: $programId, userIds: $userIds, accountId: $accountId) {\n    id\n    enrollee\n    createdAt\n  }\n}\n",
  "variables": {
    "programId": "3cd6bb00-4f03-437a-8659-8997de0b0c0d",
    "userIds": [
      "daDdICJBPnNOQEw8UXQEFJu0civhUYjqTOan3KOk",
      "bXCJd5bFRf7A84qdMIvsMa0qC2g7s7g2RxznllVj"
    ],
    "accountId": "1"
  }
}
{
  "data": {
    "batchEnrollUserToProgram": [
      {
        "id": "a1b2c3d4-0000-0000-0000-000000000001",
        "enrollee": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "createdAt": "2026-06-08T20:00:00.000Z"
      },
      {
        "id": "a1b2c3d4-0000-0000-0000-000000000002",
        "enrollee": "3f1a9c22-8d4e-4b7a-b1c0-6e2f5a9d8c31",
        "createdAt": "2026-06-08T20:00:00.000Z"
      }
    ]
  }
}

Enrolls a list of Canvas users into a single Program.

Arguments

Argument
Type
Description

programId

String (UUID)

The Journey Program to enroll users into

userIds

[String!] (Canvas UUIDs)

Canvas user UUIDs to enroll

accountId

String

Canvas account ID (usually "1")

Response fields

Field
Type
Description

id

String (UUID)

Journey enrollment record ID

enrollee

String (UUID)

Internal Journey user ID (v4 UUID) of the enrolled user — the Journey-issued ID, not the Canvas user UUID passed in userIds

createdAt

DateTime

Timestamp when the enrollment was created

Re-enrollment behavior

If a user is already enrolled in the Program, the existing enrollment is returned with no error. It is safe to include already-enrolled users in a batch.


Example Request


Batching Large Enrollments

There is no server-enforced limit on userIds length, but the cost scales with users × program requirements: each user triggers roughly 5 + (2 × R) database operations, where R is the number of requirements in the program.

Recommended batch sizes:

Program requirements
Recommended batch size

1–5

200

6–15

100

16+

50

Already-enrolled users are safely skipped (idempotent), so retrying a failed batch with the same user list is safe.

Rate limiting

The API enforces per-user rate limits. When exceeded it returns HTTP 429, which Apollo surfaces as a GraphQL error with message "Too Many Requests". The error body includes a retryAfter field (seconds) indicating how long to wait.

Recommendations for bulk jobs:

  • Sleep 3 seconds between batch calls as a baseline.

  • On a "Too Many Requests" error, read the retryAfter value from the error extensions and wait that many seconds before retrying.

  • Limit retries to 5 attempts per batch before treating it as a hard failure.


Finding a Program ID

The programId is a UUID visible in the Journey admin UI URL when viewing a Program. To look it up programmatically, use the programs GraphQL query, which returns every non-deleted Program in the account when the caller has read_as_admin. Match on name client-side.

Note: programs is part of the experimental Programs surface — its shape may change without notice.


Fetching Canvas User UUIDs

Canvas user UUIDs (not numeric IDs) are required. Fetch them from the Canvas Accounts API:

Paginate with ?page=2, ?page=3, etc. until you receive fewer than per_page results.


Error Reference

Scenario
Error

Program UUID does not exist

NotFoundException: Program not found: <id>

Program is not open for enrollment

BadRequestException: Program is not open to enroll learners

Canvas returns 403 on the permission lookup

403 Forbidden: "You do not have permission to enroll users to programs."

Caller's read_as_admin permission is false

401 Unauthorized: "You do not have permission to enroll users to programs. Required Canvas permission: read_as_admin. Contact your Canvas administrator to request access."

Invalid or expired JWT

401 Unauthorized

Too many requests

GraphQL error: "Too Many Requests" (HTTP 429) — read retryAfter from error body and wait

Last updated

Was this helpful?