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_adminpermission 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
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.
Journey JWT obtained from Canvas (POST /api/v1/jwts?audience=Instructure&workflows[]=journey, then base64-decode the token field).
GraphQL mutation string.
mutation BatchEnrollUsers($programId: String!, $userIds: [String!]!, $accountId: String!) { batchEnrollUserToProgram(programId: $programId, userIds: $userIds, accountId: $accountId) { id enrollee createdAt } }GraphQL always returns HTTP 200. Check the errors field in the response
body to determine whether the operation succeeded.
Invalid or expired JWT, or caller's read_as_admin permission is false.
Canvas denied the permission lookup for the given account.
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
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
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:
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 theretryAftervalue 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:
programsis 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
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?