Webhook documentation
Every webhook the Pass learning platform sends to providers: how they're delivered, how to verify them, and the payload for each event.
Webhooks are subscribed to at organisation level. Each event subscription has its own URL and secret. You decide whether to consume webhooks at a single endpoint or at separate endpoints per event, and whether they all share the same secret.
Webhooks only fire for users who are students (not teachers, organisation managers or admins), and who are part of an organisation that subscribes to the event. A student belongs to one organisation.
Each webhook type can be enabled or disabled at organisation level, or globally for all organisations. This is a boolean flag in each case, so no configuration is lost.
Delivery & security
Authentication
Every request carries this header:
X-CT-WEBHOOK-TOKENIts value is the secret configured for that event subscription. Reject any request where the header is missing or doesn't match, and compare it in constant time.
Timeouts
We allow 5 seconds to connect and 5 seconds for a response. A slower response counts as a failed delivery and is retried. Return a 2xx straight away and process the payload asynchronously.
Retries
If a webhook times out or receives a non-2xx response, it's queued for retry. The delay between attempts follows a power law, so it grows after each failure. Each webhook is attempted 5 times in total: the first attempt plus 4 retries.
| After failed attempt | Delay before next attempt |
|---|---|
| 1 | 1 minute |
| 2 | 8 minutes |
| 3 | 27 minutes |
| 4 | 64 minutes |
After the fifth failed attempt, delivery is abandoned. In the worst case, the last attempt arrives about 1 hour 40 minutes after the event.
Ordering, duplicates & timestamps
- Webhooks are delivered from a queue, so order isn't guaranteed. A retried webhook can arrive after a later event.
timestampis when the event happened (for example, when an attempt was completed or a user was attached), not when the webhook was sent.- Make your consumer idempotent, and use
timestampto order events for the same learner.
Wrapper format
All webhooks share a common wrapper: a version, the event type, a timestamp, and a data property holding the event-specific payload. Use type and version to route each webhook to the right processing.
{
"version": "1",
"type": "event.name",
"timestamp": "2024-11-07T11:23:22Z",
"data": {
...
}
}versionis the string"1".timestampis ISO 8601 in UTC, ending inZ.
Event index
| Event type | Description |
|---|---|
user.created | User created |
user.updated | User updated |
user.deleted | User deleted |
user.organisation.attached | User attached to organisation |
user.organisation.detached | User detached from organisation |
user.diagnostic.completed | Diagnostic (Initial Assessment) completed |
user.course.enrolled | Enrolled on course |
user.course.un-enrolled | Un-enrolled from course |
user.course.ska.completed | Subject Knowledge Assessment completed |
user.course.taka.completed | Topic Area Knowledge Assessment completed |
user.course.topic.completed | Topic completed |
user.course.recommended-topics.completed | All recommended topics completed |
user.course.exam.potentially-ready | Potentially ready for exam |
user.course.exam.no-longer-potentially-ready | No longer potentially ready for exam |
user.course.exam.ready | Marked ready for exam |
user.course.exam.not-ready | Marked not ready for exam |
Common objects
These shapes appear across several events. Each event below says which ones it uses.
Student user object
Used by user.created, user.updated, user.deleted, user.organisation.attached, user.organisation.detached and user.diagnostic.completed.
{
"id": "f3552ee4-7f62-4431-8938-fbf121bc56c1",
"firstName": "Alana",
"lastName": "Ramirez",
"username": null,
"email": "evon.burleigh@gamebox.net",
"email_enabled": true
}| Field | Type | Notes |
|---|---|---|
id | string (UUID) | |
firstName | string | Can be an empty string "". |
lastName | string | Can be an empty string "". |
username | string | null | |
email | string | null | null whenever email_enabled is false. |
email_enabled | boolean | Snake case, unlike the other keys. This is intentional and fixed for backwards compatibility. |
User reference
Used by all user.course.* events.
{
"id": "3b456c4f-050a-45c0-8975-82e66c13b1e7"
}Course object
Used by user.course.un-enrolled, user.course.ska.completed, user.course.taka.completed, and each entry of recommendedCourses in user.diagnostic.completed.
{
"id": "288e32a1-7533-46e3-a752-b9f79e6b45fd",
"name": "Functional Skills Maths Level 1",
"subject": {
"id": "b05ff81e-a35a-11ef-be12-0242ac150005",
"name": "Maths"
},
"level": {
"id": "67474bfe-2512-4ffe-a890-98e456a65d64",
"name": "Level 1",
"levelGroup": {
"id": "5828cef3-079f-4200-9b06-c5b6354eb312",
"name": "Functional Skills"
}
}
}levelis always present.levelGroupis only present when the level belongs to a group. Otherwise the key is left out entirely, rather than sent asnull.
Course-with-state object
Used by user.course.enrolled, user.course.topic.completed, user.course.recommended-topics.completed, user.course.exam.potentially-ready, user.course.exam.no-longer-potentially-ready, user.course.exam.ready and user.course.exam.not-ready.
{
"id": "288e32a1-7533-46e3-a752-b9f79e6b45fd",
"name": "Functional Skills Maths Level 1",
"subject": {
"id": "b05ff81e-a35a-11ef-be12-0242ac150005",
"name": "Maths"
},
"state": {
"plannedEndDate": null,
"completionPercentage": 2.63,
"initialLevel": 0.5,
"currentLevel": 0.5,
"timeTaken": 1718,
"recommendedTopics": {
"remaining": 36,
"completionPercentage": 0
}
}
}| Field | Type | Notes |
|---|---|---|
state.plannedEndDate | string (ISO 8601 UTC) | null | |
state.completionPercentage | number | 0–100. |
state.initialLevel | number | null | Rounded to 2 decimal places. null when current level is hidden for the course or for this learner. |
state.currentLevel | number | null | Same rules as initialLevel. |
state.timeTaken | integer | Total seconds spent on the course. |
state.recommendedTopics.remaining | integer | null | null when no SKA has been taken, so there are no recommended topics. |
state.recommendedTopics.completionPercentage | number | 0–100. |
level. Events that report progress use course-with-state; the others use the Course object, which has level but no state.Organisation object
{
"id": "c81e84c8-91da-11ef-859c-0242ac160003",
"name": "MME"
}Events
User created
user.created
Fires when a student user is created within an organisation. Providers probably only need to listen to User attached and User detached, as these give the same information when a user is created through the public API.
dataa Student user object
{
"version": "1",
"type": "user.created",
"timestamp": "2024-11-07T11:23:22Z",
"data": {
"id": "f3552ee4-7f62-4431-8938-fbf121bc56c1",
"firstName": "Alana",
"lastName": "Ramirez",
"username": null,
"email": "evon.burleigh@gamebox.net",
"email_enabled": true
}
}User updated
user.updated
When a user is updated within an organisation which has webhooks enabled, this event will fire.
dataa Student user object
This example shows a learner with email disabled: email_enabled is false and email is null.
{
"version": "1",
"type": "user.updated",
"timestamp": "2024-11-21T13:50:58Z",
"data": {
"id": "3b456c4f-050a-45c0-8975-82e66c13b1e7",
"firstName": "Mercedes",
"lastName": "Howard",
"username": "mhoward",
"email": null,
"email_enabled": false
}
}User deleted
user.deleted
Fires when a student user is deleted.
dataa Student user object, flat, in the same shape asuser.updated
{
"version": "1",
"type": "user.deleted",
"timestamp": "2024-12-02T09:14:05Z",
"data": {
"id": "3b456c4f-050a-45c0-8975-82e66c13b1e7",
"firstName": "Mercedes",
"lastName": "Howard",
"username": null,
"email": "arvilla.casebeer@fmail.co.uk",
"email_enabled": true
}
}User attached to organisation
user.organisation.attached
When a user is attached to an organisation which has webhooks enabled, this event will fire.
data.usera Student user objectdata.organisationan Organisation object
{
"version": "1",
"type": "user.organisation.attached",
"timestamp": "2024-11-07T11:23:25Z",
"data": {
"user": {
"id": "f3552ee4-7f62-4431-8938-fbf121bc56c1",
"firstName": "Alana",
"lastName": "Ramirez",
"username": null,
"email": "evon.burleigh@gamebox.net",
"email_enabled": true
},
"organisation": {
"id": "c81e84c8-91da-11ef-859c-0242ac160003",
"name": "MME"
}
}
}User detached from organisation
user.organisation.detached
When a user is detached from an organisation which has webhooks enabled, this event will fire.
data.usera Student user objectdata.organisationan Organisation object
{
"version": "1",
"type": "user.organisation.detached",
"timestamp": "2024-11-21T14:38:41Z",
"data": {
"user": {
"id": "45e34642-3f87-4197-a616-4e4cc11655bc",
"firstName": "Jessamine",
"lastName": "Leblanc",
"username": null,
"email": "kathy.villeda@luckymarmot.com",
"email_enabled": true
},
"organisation": {
"id": "c81e84c8-91da-11ef-859c-0242ac160003",
"name": "MME"
}
}
}Diagnostic (Initial Assessment) completed
user.diagnostic.completed
Suggests recommended courses and provides an initial level for the learner. The system currently suggests one recommended course, but more than one course may be recommended in the future.
data.usera Student user objectdata.diagnostic.attempt.timeTakeninteger, in secondsdata.diagnostic.attempt.initialLevelnumber, rounded to 2 decimal placesdata.recommendedCourses[]each a Course object (withlevel)
{
"version": "1",
"type": "user.diagnostic.completed",
"timestamp": "2024-11-22T10:50:17Z",
"data": {
"user": {
"id": "3b456c4f-050a-45c0-8975-82e66c13b1e7",
"firstName": "Mercedes",
"lastName": "Howard",
"username": null,
"email": "arvilla.casebeer@fmail.co.uk",
"email_enabled": true
},
"diagnostic": {
"id": "b06c9594-a35a-11ef-be12-0242ac150005",
"name": "Functional Skills Maths Initial Assessment",
"subject": {
"id": "b05ff81e-a35a-11ef-be12-0242ac150005",
"name": "Maths"
},
"attempt": {
"id": "71440974-bf03-4f61-a920-d3a1fbb2eccb",
"timeTaken": 7,
"initialLevel": 0.5
}
},
"recommendedCourses": [
{
"id": "94635c45-a8ac-41d4-baa7-4282d3357e06",
"name": "Functional Skills Maths Entry Level 3",
"subject": {
"id": "b05ff81e-a35a-11ef-be12-0242ac150005",
"name": "Maths"
},
"level": {
"id": "6e80a310-d9b9-451f-a699-e71c41a2d6db",
"name": "Entry Level 3",
"levelGroup": {
"id": "5828cef3-079f-4200-9b06-c5b6354eb312",
"name": "Functional Skills"
}
}
}
]
}
}Enrolled on course
user.course.enrolled
Provides the planned end date if supplied, plus initial level, current level, time taken on the course in seconds, and so on. Many of the stats are null or 0 when a learner first enrols, but this gives the shape of the data that comes with later progress events.
data.usera user referencedata.coursea Course-with-state object
When the current level is turned off for a course, or for a specific learner on a course, levels will be null:
{
"version": "1",
"type": "user.course.enrolled",
"timestamp": "2024-11-22T10:55:36Z",
"data": {
"user": {
"id": "3b456c4f-050a-45c0-8975-82e66c13b1e7"
},
"course": {
"id": "94635c45-a8ac-41d4-baa7-4282d3357e06",
"name": "Functional Skills Maths Entry Level 3",
"subject": {
"id": "b05ff81e-a35a-11ef-be12-0242ac150005",
"name": "Maths"
},
"state": {
"plannedEndDate": null,
"completionPercentage": 0,
"initialLevel": null,
"currentLevel": null,
"timeTaken": 1281,
"recommendedTopics": {
"remaining": null,
"completionPercentage": 0
}
}
}
}
}When the current level is enabled for the course, levels will be non-null:
{
"version": "1",
"type": "user.course.enrolled",
"timestamp": "2024-11-22T10:57:54Z",
"data": {
"user": {
"id": "3b456c4f-050a-45c0-8975-82e66c13b1e7"
},
"course": {
"id": "288e32a1-7533-46e3-a752-b9f79e6b45fd",
"name": "Functional Skills Maths Level 1",
"subject": {
"id": "b05ff81e-a35a-11ef-be12-0242ac150005",
"name": "Maths"
},
"state": {
"plannedEndDate": null,
"completionPercentage": 0,
"initialLevel": 0.5,
"currentLevel": 0.5,
"timeTaken": 0,
"recommendedTopics": {
"remaining": null,
"completionPercentage": 0
}
}
}
}
}Un-enrolled from course
user.course.un-enrolled
When a learner leaves a course.
data.usera user referencedata.coursea Course object (withlevel, nostate)
{
"version": "1",
"type": "user.course.un-enrolled",
"timestamp": "2024-11-19T13:23:22Z",
"data": {
"user": {
"id": "f3552ee4-7f62-4431-8938-fbf121bc56c1"
},
"course": {
"id": "8ef63a6e-3ad2-11eb-adc1-0242ac120002",
"name": "Functional Skills Maths Level 2",
"subject": {
"id": "b05ff81e-a35a-11ef-be12-0242ac150005",
"name": "Maths"
},
"level": {
"id": "a51c3833-32c9-4c24-972f-a9f7887f3786",
"name": "Level 2",
"levelGroup": {
"id": "5828cef3-079f-4200-9b06-c5b6354eb312",
"name": "Functional Skills"
}
}
}
}
}Subject Knowledge Assessment completed
user.course.ska.completed
Provides the learner's score, the time taken, and their recommended topics for the course, grouped by topic area.
recommendedTopicsByArea is the learner's full current set of recommended topics for the course, not only the topics produced by this SKA. It can be an empty array.
data.usera user referencedata.coursea Course object (withlevel)data.ska.attempt.scorenumber, can be fractionaldata.ska.attempt.timeTakeninteger, in seconds, ornull
{
"version": "1",
"type": "user.course.ska.completed",
"timestamp": "2024-11-20T11:17:37Z",
"data": {
"user": {
"id": "fa5a6db2-deb3-460a-be92-3b027013bfbf"
},
"course": {
"id": "288e32a1-7533-46e3-a752-b9f79e6b45fd",
"name": "Functional Skills Maths Level 1",
"subject": {
"id": "b05ff81e-a35a-11ef-be12-0242ac150005",
"name": "Maths"
},
"level": {
"id": "67474bfe-2512-4ffe-a890-98e456a65d64",
"name": "Level 1",
"levelGroup": {
"id": "5828cef3-079f-4200-9b06-c5b6354eb312",
"name": "Functional Skills"
}
}
},
"ska": {
"id": "708f6189-a728-11ef-8fb0-0242ac150005",
"name": "Maths Subject Knowledge Assessment Functional Skills Level 1",
"attempt": {
"id": "87170dcc-4789-467f-89dc-5221d8891138",
"timeTaken": 56,
"score": 6.5
}
},
"recommendedTopicsByArea": [
{
"id": "16281774-a73d-11ef-8fb0-0242ac150005",
"name": "Using Numbers",
"topics": [
{
"id": "705ff22c-a728-11ef-8fb0-0242ac150005",
"name": "Ordering Numbers"
},
{
"id": "705ff53b-a728-11ef-8fb0-0242ac150005",
"name": "Formulas"
}
]
},
{
"id": "162817b3-a73d-11ef-8fb0-0242ac150005",
"name": "Common Measures, Shape and Space",
"topics": [
{
"id": "705ff280-a728-11ef-8fb0-0242ac150005",
"name": "Length"
}
]
}
]
}
}Topic Area Knowledge Assessment completed
user.course.taka.completed
Fires when a learner completes a Topic Area Knowledge Assessment (TAKA). The payload is identical to user.course.ska.completed, except the assessment block's key is taka instead of ska.
recommendedTopicsByArea is the learner's full current set of recommended topics for the course, across all topic areas, not just the area the TAKA covered.
data.usera user referencedata.coursea Course object (withlevel)data.taka.attempt.scorenumber, can be fractionaldata.taka.attempt.timeTakeninteger, in seconds, ornull
{
"version": "1",
"type": "user.course.taka.completed",
"timestamp": "2025-02-03T15:42:10Z",
"data": {
"user": {
"id": "fa5a6db2-deb3-460a-be92-3b027013bfbf"
},
"course": {
"id": "288e32a1-7533-46e3-a752-b9f79e6b45fd",
"name": "Functional Skills Maths Level 1",
"subject": {
"id": "b05ff81e-a35a-11ef-be12-0242ac150005",
"name": "Maths"
},
"level": {
"id": "67474bfe-2512-4ffe-a890-98e456a65d64",
"name": "Level 1",
"levelGroup": {
"id": "5828cef3-079f-4200-9b06-c5b6354eb312",
"name": "Functional Skills"
}
}
},
"taka": {
"id": "01b5bca7-6a5b-43a3-b0b4-c97b27229396",
"name": "Using Numbers Topic Area Knowledge Assessment",
"attempt": {
"id": "1adb88b3-eb8d-4653-b410-5f946644dc1c",
"timeTaken": 312,
"score": 4
}
},
"recommendedTopicsByArea": [
{
"id": "16281774-a73d-11ef-8fb0-0242ac150005",
"name": "Using Numbers",
"topics": [
{
"id": "705ff22c-a728-11ef-8fb0-0242ac150005",
"name": "Ordering Numbers"
}
]
}
]
}
}Topic completed
user.course.topic.completed
The completed topic is nested in its topic area (see completed.topic).
data.usera user referencedata.coursea Course-with-state objectdata.completedthe topic area, with the completed topic intopic
If no SKA was done, there are no recommended topics, so recommendedTopics.remaining will always be null as the learner completes topics:
{
"version": "1",
"type": "user.course.topic.completed",
"timestamp": "2024-11-25T11:29:28Z",
"data": {
"user": {
"id": "3b456c4f-050a-45c0-8975-82e66c13b1e7"
},
"course": {
"id": "288e32a1-7533-46e3-a752-b9f79e6b45fd",
"name": "Functional Skills Maths Level 1",
"subject": {
"id": "b05ff81e-a35a-11ef-be12-0242ac150005",
"name": "Maths"
},
"state": {
"plannedEndDate": null,
"completionPercentage": 2.63,
"initialLevel": 0.5,
"currentLevel": 0.5,
"timeTaken": 1718,
"recommendedTopics": {
"remaining": null,
"completionPercentage": 0
}
}
},
"completed": {
"id": "16281774-a73d-11ef-8fb0-0242ac150005",
"name": "Using Numbers",
"topic": {
"id": "705ff1db-a728-11ef-8fb0-0242ac150005",
"name": "Numbers and Place Value"
}
}
}
}When an SKA has been done, the recommendedTopics stats will be non-null:
{
"version": "1",
"type": "user.course.topic.completed",
"timestamp": "2024-11-25T11:37:33Z",
"data": {
"user": {
"id": "3b456c4f-050a-45c0-8975-82e66c13b1e7"
},
"course": {
"id": "288e32a1-7533-46e3-a752-b9f79e6b45fd",
"name": "Functional Skills Maths Level 1",
"subject": {
"id": "b05ff81e-a35a-11ef-be12-0242ac150005",
"name": "Maths"
},
"state": {
"plannedEndDate": null,
"completionPercentage": 2.63,
"initialLevel": 0.5,
"currentLevel": 0.5,
"timeTaken": 1792,
"recommendedTopics": {
"remaining": 36,
"completionPercentage": 0
}
}
},
"completed": {
"id": "16281774-a73d-11ef-8fb0-0242ac150005",
"name": "Using Numbers",
"topic": {
"id": "705ff1db-a728-11ef-8fb0-0242ac150005",
"name": "Numbers and Place Value"
}
}
}
}All recommended topics completed
user.course.recommended-topics.completed
When all recommended topics have been completed.
data.usera user referencedata.coursea Course-with-state object
{
"version": "1",
"type": "user.course.recommended-topics.completed",
"timestamp": "2024-11-27T12:26:57Z",
"data": {
"user": {
"id": "9abffa41-8207-4273-abb2-78539af70f1b"
},
"course": {
"id": "288e32a1-7533-46e3-a752-b9f79e6b45fd",
"name": "Functional Skills Maths Level 1",
"subject": {
"id": "b05ff81e-a35a-11ef-be12-0242ac150005",
"name": "Maths"
},
"state": {
"plannedEndDate": null,
"completionPercentage": 76.63,
"initialLevel": 1.0,
"currentLevel": 2.6,
"timeTaken": 71566,
"recommendedTopics": {
"remaining": 0,
"completionPercentage": 100
}
}
}
}
}Potentially ready for exam
user.course.exam.potentially-ready
This event is triggered when a learner makes enough progress that they are potentially ready for their exam, based on a set of criteria Complete Tuition has determined for the given course.
A learner can trigger this webhook more than once. This happens if they initially meet the criteria, then fall below them after retaking a component, and later meet them again.
data.usera user referencedata.coursea Course-with-state object
{
"version": "1",
"type": "user.course.exam.potentially-ready",
"timestamp": "2024-11-28T08:59:11Z",
"data": {
"user": {
"id": "4e99ec5f-9552-4d1c-95bf-31ae44d53249"
},
"course": {
"id": "c9929c63-0978-4887-83c7-4124d5863702",
"name": "Functional Skills English Level 1",
"subject": {
"id": "b06007ae-a35a-11ef-be12-0242ac150005",
"name": "English"
},
"state": {
"plannedEndDate": "2024-12-10T00:00:00Z",
"completionPercentage": 92.1,
"initialLevel": 1.7,
"currentLevel": 2.1,
"timeTaken": 71566,
"recommendedTopics": {
"remaining": 0,
"completionPercentage": 100
}
}
}
}
}No longer potentially ready for exam
user.course.exam.no-longer-potentially-ready
This event is triggered when a learner who had previously been potentially ready for their exam goes backwards and is no longer potentially exam ready.
A learner can trigger this webhook more than once. This happens if they initially meet the criteria, then fall below them after retaking a component, and repeat.
data.usera user referencedata.coursea Course-with-state object
{
"version": "1",
"type": "user.course.exam.no-longer-potentially-ready",
"timestamp": "2024-12-10T10:41:24Z",
"data": {
"user": {
"id": "e7ecdf42-3942-4aa1-a1dd-df05baaecc06"
},
"course": {
"id": "8a467681-05d3-4398-8020-8371b326402e",
"name": "Functional Skills English Level 2",
"subject": {
"id": "b06007ae-a35a-11ef-be12-0242ac150005",
"name": "English"
},
"state": {
"plannedEndDate": null,
"completionPercentage": 10.84,
"initialLevel": 2.7,
"currentLevel": 2.3,
"timeTaken": 319,
"recommendedTopics": {
"remaining": 15,
"completionPercentage": 11.76
}
}
}
}
}Marked ready for exam
user.course.exam.ready
A provider or teacher marked a student as ready for their exam.
data.usera user referencedata.coursea Course-with-state objectdata.by.atwhen the learner was marked ready, ISO 8601 UTCdata.by.userthe teacher or provider user who marked them, not the student
In by.user, email is always present. firstName and lastName can be empty strings "".
{
"version": "1",
"type": "user.course.exam.ready",
"timestamp": "2024-11-26T14:49:56Z",
"data": {
"user": {
"id": "40a21ac4-2989-458e-9652-aee0749bdf53"
},
"course": {
"id": "c9929c63-0978-4887-83c7-4124d5863702",
"name": "Functional Skills English Level 1",
"subject": {
"id": "b06007ae-a35a-11ef-be12-0242ac150005",
"name": "English"
},
"state": {
"plannedEndDate": "2024-11-19T00:00:00Z",
"completionPercentage": 0,
"initialLevel": 0.5,
"currentLevel": 1.7,
"timeTaken": 71566,
"recommendedTopics": {
"remaining": 0,
"completionPercentage": 100
}
}
},
"by": {
"at": "2024-11-26T14:49:56Z",
"user": {
"id": "c732120d-91d7-11ef-859c-0242ac160003",
"firstName": "Dave",
"lastName": "Smith",
"email": "dave.smith+teacher@company.co.uk"
}
}
}
}Marked not ready for exam
user.course.exam.not-ready
A provider or teacher marked a student as not ready for their exam, having previously marked them as ready.
Unlike user.course.exam.ready, this event has no by block.
data.usera user referencedata.coursea Course-with-state object
{
"version": "1",
"type": "user.course.exam.not-ready",
"timestamp": "2024-11-26T13:34:13Z",
"data": {
"user": {
"id": "40a21ac4-2989-458e-9652-aee0749bdf53"
},
"course": {
"id": "c9929c63-0978-4887-83c7-4124d5863702",
"name": "Functional Skills English Level 1",
"subject": {
"id": "b06007ae-a35a-11ef-be12-0242ac150005",
"name": "English"
},
"state": {
"plannedEndDate": "2024-11-19T00:00:00Z",
"completionPercentage": 84.21,
"initialLevel": 0.5,
"currentLevel": 1.7,
"timeTaken": 71566,
"recommendedTopics": {
"remaining": 0,
"completionPercentage": 100
}
}
}
}
}