{
  "openapi": "3.1.1",
  "info": {
    "title": "Evercate.Web | v1",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api-v1.evercate.com/"
    }
  ],
  "paths": {
    "/users/{id}/courses/{courseId}/activity": {
      "get": {
        "tags": [
          "Activity"
        ],
        "summary": "The user's activity in a course, oldest first — the same entries the\ncourse.activity webhook pushes (lesson completions, quiz attempts with\nscores, course started/completed). A course reset archives the old run;\npass includeArchived=true to get those entries too, flagged Archived.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Numeric user id, as in /users.",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "courseId",
            "in": "path",
            "description": "Course guid, as in /courses.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "includeArchived",
            "in": "query",
            "description": "Include activity from archived (pre-reset) runs.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/users/{id}/courses/{courseId}/certificate": {
      "get": {
        "tags": [
          "Certificate"
        ],
        "summary": "Returns the user's certificate PDF for a completed course. Completion is the\nonly qualification: Course.SendCertificate merely controls whether the PDF is\nEMAILED on completion, and rendering works for any course, so the API serves\nthe certificate either way. 404 when the course is not completed.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Numeric user id, as in /users.",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "courseId",
            "in": "path",
            "description": "Course guid, as in /courses.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/users/{id}/courses": {
      "get": {
        "tags": [
          "CourseAccess"
        ],
        "summary": "All course accesses for a user — including expired and revoked-by-expiry ones\n(HasAccess tells them apart), each with completion state and timestamps.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Numeric user id, as in /users.",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/users/{id}/courses/{courseId}": {
      "get": {
        "tags": [
          "CourseAccess"
        ],
        "summary": "A single user's access to a single course, with completion state and\ntimestamps. 404 when the user was never given access.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Numeric user id, as in /users.",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "courseId",
            "in": "path",
            "description": "Course guid, as in /courses.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/courses/{courseId}/users": {
      "get": {
        "tags": [
          "CourseAccess"
        ],
        "summary": "All accesses to a course — including expired ones — each with the user,\ncompletion state and timestamps. The course-side view of /users/{id}/courses.",
        "parameters": [
          {
            "name": "courseId",
            "in": "path",
            "description": "Course guid, as in /courses.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/courses": {
      "get": {
        "tags": [
          "Course"
        ],
        "summary": "Get all courses (scoped to token's account/license).",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/courses/{courseId}/invite/{userId}": {
      "post": {
        "tags": [
          "Course"
        ],
        "summary": "Send a course invitation to the specified user.\nThe user must already have access to the course.",
        "parameters": [
          {
            "name": "courseId",
            "in": "path",
            "description": "Public ID (GUID string) of the course.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "userId",
            "in": "path",
            "description": "Numeric ID of the user.",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/tags/{id}": {
      "get": {
        "tags": [
          "Tag"
        ],
        "summary": "Get a tag by id.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "put": {
        "tags": [
          "Tag"
        ],
        "summary": "Update a tag.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TagUpdateModel"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/TagUpdateModel"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/TagUpdateModel"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/tags": {
      "post": {
        "tags": [
          "Tag"
        ],
        "summary": "Create a new tag.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TagCreateModel"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/TagCreateModel"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/TagCreateModel"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/team/{id}": {
      "get": {
        "tags": [
          "Team"
        ],
        "summary": "Get a team by id.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/teams": {
      "post": {
        "tags": [
          "Team"
        ],
        "summary": "Create a new team.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeamCreateModel"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/TeamCreateModel"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/TeamCreateModel"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/teams/{id}": {
      "put": {
        "tags": [
          "Team"
        ],
        "summary": "Update a team.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeamUpdateModel"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/TeamUpdateModel"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/TeamUpdateModel"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "delete": {
        "tags": [
          "Team"
        ],
        "summary": "Delete a team.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/users/{id}": {
      "get": {
        "tags": [
          "User"
        ],
        "summary": "Get a single user by id.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "put": {
        "tags": [
          "User"
        ],
        "summary": "Update a user.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserUpdateModel"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/UserUpdateModel"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/UserUpdateModel"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "delete": {
        "tags": [
          "User"
        ],
        "summary": "Delete a user.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/users/{email}": {
      "get": {
        "tags": [
          "User"
        ],
        "summary": "Get a single user by email/username.\nThe email should be url-encoded, e.g. \"john.doe%40company.com\".",
        "parameters": [
          {
            "name": "email",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/users": {
      "get": {
        "tags": [
          "User"
        ],
        "summary": "Get all users (scoped to token's account/license).",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "post": {
        "tags": [
          "User"
        ],
        "summary": "Create a new user within the token's license.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserCreateModel"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/UserCreateModel"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/UserCreateModel"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/users/{email}/sso/{ttl}/{ip}": {
      "get": {
        "tags": [
          "User"
        ],
        "summary": "Gets a Single Sign-On (SSO) URL for the user identified by email.\nGenerating a SSO URL will invalidate any previous URL. All SSO URLs are one-time use.",
        "parameters": [
          {
            "name": "email",
            "in": "path",
            "description": "The user's email address (url-encoded).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ttl",
            "in": "path",
            "description": "How long (in minutes) the URL should be valid — must be between 1 and 1440.",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "ip",
            "in": "path",
            "description": "IP lock. Use \"0.0.0.0\" to allow any IP.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/users/{userId}/sso/{ttl}/{ip}": {
      "get": {
        "tags": [
          "User"
        ],
        "summary": "Gets a Single Sign-On (SSO) URL for the user identified by id.\nGenerating a SSO URL will invalidate any previous URL. All SSO URLs are one-time use.",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "description": "The user's id.",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "ttl",
            "in": "path",
            "description": "How long (in minutes) the URL should be valid — must be between 1 and 1440.",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "ip",
            "in": "path",
            "description": "IP lock. Use \"0.0.0.0\" to allow any IP.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/usergroups": {
      "get": {
        "tags": [
          "UserGroup"
        ],
        "summary": "Get all user groups (scoped to token's account/license, excluding Archive groups).",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "tags": [
          "Webhook"
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "post": {
        "tags": [
          "Webhook"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreateModel"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreateModel"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreateModel"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/webhooks/{publicId}": {
      "get": {
        "tags": [
          "Webhook"
        ],
        "parameters": [
          {
            "name": "publicId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "put": {
        "tags": [
          "Webhook"
        ],
        "parameters": [
          {
            "name": "publicId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdateModel"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdateModel"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdateModel"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhook"
        ],
        "parameters": [
          {
            "name": "publicId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "TagCreateModel": {
        "required": [
          "tagTypeId",
          "label"
        ],
        "type": "object",
        "properties": {
          "tagTypeId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "label": {
            "type": "string"
          }
        }
      },
      "TagUpdateModel": {
        "required": [
          "label"
        ],
        "type": "object",
        "properties": {
          "label": {
            "type": "string"
          }
        }
      },
      "TeamCreateModel": {
        "required": [
          "teamLeaderId"
        ],
        "type": "object",
        "properties": {
          "teamLeaderId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "teamName": {
            "type": [
              "null",
              "string"
            ]
          },
          "canCreateTeamMember": {
            "type": "boolean"
          },
          "remainingUserCreateQuota": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "definingTags": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          "managedTags": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          }
        }
      },
      "TeamUpdateModel": {
        "type": "object",
        "properties": {
          "teamName": {
            "type": [
              "null",
              "string"
            ]
          },
          "canCreateTeamMember": {
            "type": "boolean"
          },
          "remainingUserCreateQuota": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "definingTags": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          "managedTags": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          }
        }
      },
      "UserCreateModel": {
        "required": [
          "username",
          "firstName",
          "lastName",
          "groupId"
        ],
        "type": "object",
        "properties": {
          "username": {
            "type": "string"
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "externalId": {
            "type": [
              "null",
              "string"
            ]
          },
          "phone": {
            "type": [
              "null",
              "string"
            ]
          },
          "notes": {
            "type": [
              "null",
              "string"
            ]
          },
          "locale": {
            "maxLength": 5,
            "minLength": 0,
            "type": [
              "null",
              "string"
            ]
          },
          "groupId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "userTags": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          }
        }
      },
      "UserUpdateModel": {
        "required": [
          "existingUsername",
          "firstName",
          "lastName",
          "groupId"
        ],
        "type": "object",
        "properties": {
          "existingUsername": {
            "type": "string"
          },
          "newUsername": {
            "type": [
              "null",
              "string"
            ]
          },
          "firstName": {
            "type": "string"
          },
          "lastName": {
            "type": "string"
          },
          "externalId": {
            "type": [
              "null",
              "string"
            ]
          },
          "phone": {
            "type": [
              "null",
              "string"
            ]
          },
          "notes": {
            "type": [
              "null",
              "string"
            ]
          },
          "locale": {
            "maxLength": 5,
            "minLength": 0,
            "type": [
              "null",
              "string"
            ]
          },
          "groupId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "userTags": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          }
        }
      },
      "WebhookCreateModel": {
        "required": [
          "url"
        ],
        "type": "object",
        "properties": {
          "url": {
            "maxLength": 1000,
            "minLength": 0,
            "type": [
              "null",
              "string"
            ]
          },
          "notificationEmail": {
            "maxLength": 256,
            "minLength": 0,
            "type": [
              "null",
              "string"
            ]
          },
          "eventTypes": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Event types to receive. Omitted or null = all events."
          }
        }
      },
      "WebhookUpdateModel": {
        "type": "object",
        "properties": {
          "url": {
            "maxLength": 1000,
            "minLength": 0,
            "type": [
              "null",
              "string"
            ]
          },
          "notificationEmail": {
            "maxLength": 256,
            "minLength": 0,
            "type": [
              "null",
              "string"
            ]
          },
          "active": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "eventTypes": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Event types to receive. Omitted/null = unchanged."
          }
        }
      }
    },
    "securitySchemes": {
      "Bearer": {
        "type": "http",
        "description": "Paste an API token. Sent as: Authorization: Bearer <token>",
        "scheme": "bearer"
      }
    }
  },
  "security": [
    {
      "Bearer": [ ]
    }
  ],
  "tags": [
    {
      "name": "Activity"
    },
    {
      "name": "Certificate"
    },
    {
      "name": "CourseAccess"
    },
    {
      "name": "Course"
    },
    {
      "name": "Tag"
    },
    {
      "name": "Team"
    },
    {
      "name": "User"
    },
    {
      "name": "UserGroup"
    },
    {
      "name": "Webhook"
    }
  ]
}