Skip to content

Developers

Wislab API

Connect your academy to your systems: sync people from your HR system or CRM, read courses, progress, certificates, enrollments and payments, and send leads to your CRM.

Base URL
https://wislab.app/api/v1
Version
v1 · 1.0.0
Download OpenAPI

The API comes with the Cohorts and Enterprise plans.

Introduction

The Wislab API is REST: resource URLs, JSON in requests and responses, and standard HTTP status codes.

Every request acts on one academy: the one your API key belongs to. The academy never goes in the URL.

The full contract is in OpenAPI 3.0. Import it into Postman, Insomnia or your client generator.

Base URL
https://wislab.app/api/v1

Authentication

  1. 1In your academy's admin, go to Settings > Integrations > API keys. Only the owner or an admin can create one.
  2. 2Pick the scopes your integration needs. The key is shown only once: store it in your secrets manager.
  3. 3Send the key in the Authorization: Bearer header of every request.

Your key is a secret

Use it only from your server. Never put it in a browser, a mobile app or a repository. If it leaks, revoke it from the same screen and create a new one.

Request
curl https://wislab.app/api/v1/academy \
  -H "Authorization: Bearer $WISLAB_API_KEY"

Scopes

Each key carries only the scopes you gave it. Use one key per integration so you can revoke one without touching the rest.

Scopes
ScopeWhat it allows
users:readRead the academy's people and their profile.
users:syncCreate, update and deactivate people from your system.
courses:readRead the course catalog.
progress:readRead each person's progress in their courses.
completions:readRead who finished each course and when.
certificates:readRead issued certificates.
bootcamps:readRead bootcamps: dates, prices and seats.
enrollments:readRead who is enrolled in each bootcamp.
payments:readRead course and workshop purchases, and receipts.
crm:readRead CRM contacts and deals.
crm:writeCreate and update CRM contacts.
fields:readRead custom fields and their values.
fields:writeWrite custom field values.

GET /academy needs no scope: any valid key can read it.

Response 403
{
  "error": "FORBIDDEN",
  "message": "API key lacks required scope: users:read."
}

Pagination

List endpoints take page (starting at 0) and limit (20 by default, up to 100).

The response has data with the results, plus total, page and pageCount. Keep requesting pages until page reaches pageCount - 1.

Request
curl "https://wislab.app/api/v1/courses?page=1&limit=50" \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": [
    "…"
  ],
  "total": 132,
  "page": 1,
  "pageCount": 3
}

Errors

An error responds with an HTTP status and a JSON body with error (a stable code you can branch on), message and sometimes details.

Errors
CodeHTTPWhat it means
VALIDATION_ERROR400A parameter or the body is invalid. details says which.
UNKNOWN_FIELD400You wrote a custom field that has no definition.
LIMIT_REACHED400The academy reached a limit of its plan.
UNAUTHORIZED401The API key is missing, malformed or revoked.
FORBIDDEN403The key lacks the scope, or the plan doesn't include the API.
NAMESPACE_FORBIDDEN403You tried to write fields in another namespace.
NOT_FOUND404The resource doesn't exist in this academy.
CONFLICT409A resource with that key already exists.
CRM_DISABLED409The academy has the CRM turned off.
RATE_LIMITED429You hit the rate limit. Wait Retry-After seconds.
INTERNAL500Something failed on our side. Retry in a few seconds.
Response 400
{
  "error": "VALIDATION_ERROR",
  "details": {
    "errors": [],
    "properties": {
      "limit": {
        "errors": [
          "Too small: expected number to be >=1"
        ]
      }
    }
  }
}

Rate limits

Each key can make 120 requests per minute. Over that you get 429 RATE_LIMITED with a Retry-After header: wait that many seconds and retry.

Response 429
HTTP/1.1 429 Too Many Requests
Retry-After: 18

{ "error": "RATE_LIMITED", "message": "Rate limit exceeded." }

Reference

Academy

The academy behind the key or token.

GET/v1/academy

Retrieve the academy

Needs no scope: any valid API key or installation token can read it. Name, slug, domain, languages, currency, plan and brand; never people.

Scope:No scope

Responses

  • 200The academy.
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/academy \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": {
    "id": "clx8k2m0p0001",
    "name": "Academia Ejemplo",
    "slug": "fundamentos-de-fotografia",
    "domain": "string",
    "url": "https://academia.example.com",
    "defaultLocale": "string",
    "supportedLocales": [
      "string"
    ],
    "currency": "USD",
    "package": "string",
    "logoUrl": "https://academia.example.com",
    "primaryColor": "string"
  }
}

Reference

Users

People in the academy, and sync from your systems.

GET/v1/users

List users

Requires the users:read scope.

Scope:users:read

Query parameters

  • pageintegeroptional

    Page number, starting at 0.

  • limitintegeroptional

    Page size, up to 100.

  • rolestringoptional

    Filter by role.

    One of: owner, admin, manager, teacher, moderator, student

  • searchstringoptional

    Search first name, last name and email.

Responses

  • 200A page of users.
  • 400Invalid query parameters (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/users \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": [
    {
      "id": "clx8k2m0p0001",
      "email": "ana@example.com",
      "firstName": "Ana",
      "lastName": "García",
      "username": "anagarcia",
      "role": "owner",
      "isBanned": false,
      "createdAt": "2026-10-27T15:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 0,
  "pageCount": 1
}
GET/v1/users/{id}

Retrieve a user

Requires the users:read scope.

Scope:users:read

Path parameters

  • idstringrequired

    The user's ID.

Responses

  • 200The user.
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 404No user with that ID in this academy.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/users/clx8k2m0p0001 \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": {
    "id": "clx8k2m0p0001",
    "email": "ana@example.com",
    "firstName": "Ana",
    "lastName": "García",
    "username": "anagarcia",
    "role": "owner",
    "isBanned": false,
    "createdAt": "2026-10-27T15:00:00.000Z"
  }
}
POST/v1/users/sync

Sync users

Creates, updates and deactivates students from your HR system or CRM, up to 500 per request. Idempotent by email. status: inactive deactivates (reversible). It only manages students: an email that belongs to a team member is reported as skipped. Requires the users:sync scope.

Scope:users:sync

Body

  • usersobject[]required
    • emailstringrequired
    • externalIdstringoptional

      The ID in your source system (employee ID, tax ID). Stored in the user's metadata.

    • firstNamestringoptional
    • lastNamestringoptional
    • rolestringoptional

      One of: student

    • statusstringoptional

      inactive deactivates the user (reversible) and keeps their history.

      One of: active, inactive

  • optionsobjectoptional
    • dryRunbooleanoptional

      Validate and report without writing anything.

    • sendWelcomeEmailbooleanoptional

Responses

  • 200The sync result.
  • 400Invalid body (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl -X POST https://wislab.app/api/v1/users/sync \
  -H "Authorization: Bearer $WISLAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "users": [
    {
      "email": "ana@example.com",
      "externalId": "EMP-1042",
      "firstName": "Ana",
      "lastName": "García",
      "role": "student",
      "status": "active"
    }
  ],
  "options": {
    "dryRun": false,
    "sendWelcomeEmail": false
  }
}'
Response 200
{
  "data": {
    "processed": 1,
    "created": 1,
    "updated": 1,
    "deactivated": 1,
    "skipped": 1,
    "dryRun": false,
    "errors": [
      {
        "index": 1,
        "email": "ana@example.com",
        "reason": "Invalid email."
      }
    ],
    "importLogId": "clx8k2m0p0002"
  }
}

Reference

Courses

The course catalog.

GET/v1/courses

List courses

Requires the courses:read scope.

Scope:courses:read

Query parameters

  • pageintegeroptional

    Page number, starting at 0.

  • limitintegeroptional

    Page size, up to 100.

  • isPublishedstringoptional

    Filter by publication status.

    One of: true, false

Responses

  • 200A page of courses.
  • 400Invalid query parameters (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/courses \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": [
    {
      "id": "clx8k2m0p0001",
      "title": "Fundamentos de fotografía",
      "slug": "fundamentos-de-fotografia",
      "description": null,
      "courseType": "course",
      "level": "basic",
      "monetizationType": "free",
      "priceInCents": 4900,
      "currency": "USD",
      "isPublished": true,
      "totalCapsules": 1,
      "createdAt": "2026-10-27T15:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 0,
  "pageCount": 1
}

Reference

Progress

Student progress.

GET/v1/users/{id}/progress

Retrieve a user's progress

Progress per course. Requires the progress:read scope.

Scope:progress:read

Path parameters

  • idstringrequired

    The user's ID.

Responses

  • 200The user's progress per course.
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 404No user with that ID in this academy.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/users/clx8k2m0p0001/progress \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": {
    "userId": "clx8k2m0p0002",
    "lessonsCompletedTotal": 1,
    "courses": [
      {
        "courseId": "clx8k2m0p0002",
        "percentageProgress": 60,
        "isEnrolled": true,
        "updatedAt": "2026-10-27T15:00:00.000Z"
      }
    ]
  }
}

Reference

Completions

Completed courses.

GET/v1/completions

List completions

Courses a student finished at 100%. Requires the completions:read scope.

Scope:completions:read

Query parameters

  • pageintegeroptional

    Page number, starting at 0.

  • limitintegeroptional

    Page size, up to 100.

  • fromdatetimeoptional

    Inclusive ISO 8601 date-time, compared with the completion date.

  • todatetimeoptional

    Inclusive ISO 8601 date-time.

Responses

  • 200A page of completions.
  • 400Invalid query parameters (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/completions \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": [
    {
      "userId": "clx8k2m0p0002",
      "courseId": "clx8k2m0p0002",
      "completedAt": "2026-10-27T15:00:00.000Z",
      "certificateId": "clx8k2m0p0002"
    }
  ],
  "total": 1,
  "page": 0,
  "pageCount": 1
}

Reference

Certificates

Issued certificates.

GET/v1/certificates

List certificates

Requires the certificates:read scope.

Scope:certificates:read

Query parameters

  • pageintegeroptional

    Page number, starting at 0.

  • limitintegeroptional

    Page size, up to 100.

  • statusstringoptional

    Filter by status.

    One of: active, revoked

  • userIdstringoptional

    Filter by user.

Responses

  • 200A page of certificates.
  • 400Invalid query parameters (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/certificates \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": [
    {
      "id": "clx8k2m0p0001",
      "userId": "clx8k2m0p0002",
      "courseId": "clx8k2m0p0002",
      "pathId": "clx8k2m0p0002",
      "bootcampId": "clx8k2m0p0002",
      "programId": "clx8k2m0p0002",
      "credentialCode": "WSL-7F3K-2Q9P",
      "issuedAt": "2026-10-27T15:00:00.000Z",
      "expiresAt": "2026-10-27T15:00:00.000Z",
      "status": "active",
      "certificateUrl": "https://academia.example.com/certificates/WSL-7F3K-2Q9P",
      "recipientName": "Ana García"
    }
  ],
  "total": 1,
  "page": 0,
  "pageCount": 1
}

Reference

Bootcamps

The academy's bootcamps.

GET/v1/bootcamps

List bootcamps

Requires the bootcamps:read scope.

Scope:bootcamps:read

Query parameters

  • pageintegeroptional

    Page number, starting at 0.

  • limitintegeroptional

    Page size, up to 100.

  • isListedstringoptional

    Filter by catalog visibility.

    One of: true, false

  • isActivestringoptional

    Filter by active status.

    One of: true, false

Responses

  • 200A page of bootcamps.
  • 400Invalid query parameters (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/bootcamps \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": [
    {
      "id": "clx8k2m0p0001",
      "title": "Fundamentos de fotografía",
      "slug": "fundamentos-de-fotografia",
      "cohortStartDate": "2026-10-27T15:00:00.000Z",
      "cohortEndDate": "2026-10-27T15:00:00.000Z",
      "enrollmentDeadline": null,
      "isFree": false,
      "regularPrice": 1,
      "earlyBirdPrice": 1,
      "currency": "USD",
      "maxStudents": 1,
      "isActive": true,
      "isEnrollmentOpen": false,
      "isListed": true,
      "createdAt": "2026-10-27T15:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 0,
  "pageCount": 1
}

Reference

Enrollments

Bootcamp enrollments.

GET/v1/enrollments

List bootcamp enrollments

Requires the enrollments:read scope.

Scope:enrollments:read

Query parameters

  • pageintegeroptional

    Page number, starting at 0.

  • limitintegeroptional

    Page size, up to 100.

  • bootcampIdstringoptional

    Filter by bootcamp.

  • userIdstringoptional

    Filter by user.

  • statusstringoptional

    Filter by status.

    One of: active, expired, completed, refunded, removed

Responses

  • 200A page of enrollments.
  • 400Invalid query parameters (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/enrollments \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": [
    {
      "id": "clx8k2m0p0001",
      "userId": "clx8k2m0p0002",
      "bootcampId": "clx8k2m0p0002",
      "status": "active",
      "enrollmentDate": "2026-10-27T15:00:00.000Z",
      "amount": 490,
      "currency": "USD",
      "isManualAssignment": false,
      "createdAt": "2026-10-27T15:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 0,
  "pageCount": 1
}

Reference

Payments

Course and workshop purchases, and payment receipts.

GET/v1/payments

List course and workshop purchases

Requires the payments:read scope.

Scope:payments:read

Query parameters

  • pageintegeroptional

    Page number, starting at 0.

  • limitintegeroptional

    Page size, up to 100.

  • kindstringoptional

    Filter by what was bought.

    One of: course, workshop

  • userIdstringoptional

    Filter by user.

  • statusstringoptional

    Filter by status.

Responses

  • 200A page of purchases.
  • 400Invalid query parameters (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/payments \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": [
    {
      "id": "clx8k2m0p0001",
      "kind": "course",
      "itemId": "clx8k2m0p0002",
      "userId": "clx8k2m0p0002",
      "amount": 490,
      "currency": "USD",
      "status": "active",
      "stripeSessionId": "cs_live_a1B2c3D4",
      "refundedAt": "2026-10-27T15:00:00.000Z",
      "createdAt": "2026-10-27T15:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 0,
  "pageCount": 1
}
GET/v1/payments/receipts

List payment receipts

Requires the payments:read scope.

Scope:payments:read

Query parameters

  • pageintegeroptional

    Page number, starting at 0.

  • limitintegeroptional

    Page size, up to 100.

  • userIdstringoptional

    Filter by user.

  • enrollmentIdstringoptional

    Filter by enrollment.

  • paymentStatusstringoptional

    Filter by payment status.

Responses

  • 200A page of receipts.
  • 400Invalid query parameters (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/payments/receipts \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": [
    {
      "id": "clx8k2m0p0001",
      "userId": "clx8k2m0p0002",
      "enrollmentId": "clx8k2m0p0002",
      "programEnrollmentId": "clx8k2m0p0002",
      "amount": 490,
      "currency": "USD",
      "paymentStatus": "paid",
      "paymentMethod": "card",
      "paidAt": "2026-10-27T15:00:00.000Z",
      "description": null,
      "isRefunded": false,
      "isCancelled": false,
      "createdAt": "2026-10-27T15:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 0,
  "pageCount": 1
}

Reference

CRM

CRM contacts and deals.

GET/v1/crm/contacts

List CRM contacts

Requires the crm:read scope.

Scope:crm:read

Query parameters

  • pageintegeroptional

    Page number, starting at 0.

  • limitintegeroptional

    Page size, up to 100.

  • lifecycleStagestringoptional

    Filter by funnel stage.

  • sourcestringoptional

    Filter by source.

  • updatedAfterdatetimeoptional

    Only contacts updated after this ISO 8601 date-time.

Responses

  • 200A page of contacts.
  • 400Invalid query parameters (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/crm/contacts \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": [
    {
      "id": "clx8k2m0p0001",
      "email": "ana@example.com",
      "phone": "+52 55 0000 0000",
      "firstName": "Ana",
      "lastName": "García",
      "jobTitle": null,
      "lifecycleStage": "SUBSCRIBER",
      "source": "SIGNUP",
      "sourceDetail": "Formulario del taller",
      "score": 1,
      "companyId": "clx8k2m0p0002",
      "tenantUserId": "clx8k2m0p0002",
      "lastContactedAt": "2026-10-27T15:00:00.000Z",
      "createdAt": "2026-10-27T15:00:00.000Z",
      "updatedAt": "2026-10-27T15:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 0,
  "pageCount": 1
}
POST/v1/crm/contacts

Create or update a CRM contact

Goes through the same intake as the academy's forms: dedupes by email, the funnel stage only moves forward, opens a deal if the academy creates them automatically, and emits lead.created when the contact is new. 201 when new, 200 when it already existed. 409 CRM_DISABLED when the academy has the CRM off. Requires the crm:write scope.

Scope:crm:write

Body

  • emailstringrequired
  • firstNamestring | nulloptional
  • lastNamestring | nulloptional
  • phonestring | nulloptional
  • sourcestringoptional

    How the person arrived. Kept from the first time the contact was seen.

    One of: SIGNUP, NEWSLETTER, WAITLIST, CONTACT_FORM, DEMO_REQUEST, COURSE_ENROLLMENT, MANUAL, IMPORT, OTHER

  • sourceDetailstring | nulloptional

    Free text about the origin, e.g. the form or campaign name.

  • lifecycleStagestringoptional

    An existing contact only moves forward in the funnel.

    One of: SUBSCRIBER, LEAD, MQL, SQL, OPPORTUNITY, CUSTOMER, EVANGELIST, OTHER

Responses

  • 200The contact already existed and was updated.
  • 201Contact created.
  • 400Invalid body (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 409The academy has the CRM off.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl -X POST https://wislab.app/api/v1/crm/contacts \
  -H "Authorization: Bearer $WISLAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "ana@example.com",
  "firstName": "Ana",
  "lastName": "García",
  "phone": "+52 55 0000 0000",
  "source": "OTHER",
  "sourceDetail": "Formulario del taller",
  "lifecycleStage": "LEAD"
}'
Response 200
{
  "data": {
    "id": "clx8k2m0p0001",
    "email": "ana@example.com",
    "phone": "+52 55 0000 0000",
    "firstName": "Ana",
    "lastName": "García",
    "jobTitle": null,
    "lifecycleStage": "SUBSCRIBER",
    "source": "SIGNUP",
    "sourceDetail": "Formulario del taller",
    "score": 1,
    "companyId": "clx8k2m0p0002",
    "tenantUserId": "clx8k2m0p0002",
    "lastContactedAt": "2026-10-27T15:00:00.000Z",
    "createdAt": "2026-10-27T15:00:00.000Z",
    "updatedAt": "2026-10-27T15:00:00.000Z"
  }
}
GET/v1/crm/deals

List CRM deals

Requires the crm:read scope.

Scope:crm:read

Query parameters

  • pageintegeroptional

    Page number, starting at 0.

  • limitintegeroptional

    Page size, up to 100.

  • statusstringoptional

    Filter by status.

    One of: OPEN, WON, LOST

  • pipelineIdstringoptional

    Filter by pipeline.

  • contactIdstringoptional

    Filter by contact.

Responses

  • 200A page of deals.
  • 400Invalid query parameters (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/crm/deals \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": [
    {
      "id": "clx8k2m0p0001",
      "title": "Fundamentos de fotografía",
      "pipelineId": "clx8k2m0p0002",
      "stageId": "clx8k2m0p0003",
      "status": "OPEN",
      "valueCents": 4900,
      "currency": "USD",
      "contactId": "clx8k2m0p0002",
      "companyId": "clx8k2m0p0002",
      "expectedCloseDate": "2026-10-27T15:00:00.000Z",
      "wonAt": "2026-10-27T15:00:00.000Z",
      "lostAt": "2026-10-27T15:00:00.000Z",
      "createdAt": "2026-10-27T15:00:00.000Z",
      "updatedAt": "2026-10-27T15:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 0,
  "pageCount": 1
}

Reference

Custom fields

Custom fields on courses, bootcamps and paths, and their values.

GET/v1/fields

List custom field definitions

Includes the academy's fields (academy) and those of each installed app (app--<handle>). Requires the fields:read scope.

Scope:fields:read

Query parameters

  • pageintegeroptional

    Page number, starting at 0.

  • limitintegeroptional

    Page size, up to 100.

  • entitystringoptional

    Filter by entity.

    One of: course, bootcamp, path

Responses

  • 200A page of definitions.
  • 400Invalid query parameters (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/fields \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": [
    {
      "id": "clx8k2m0p0001",
      "entity": "course",
      "namespace": "academy",
      "key": "level",
      "fieldId": "academy:level",
      "type": "text",
      "label": {
        "en": "Level",
        "es": "Nivel"
      },
      "options": [
        {
          "value": "string",
          "label": "string"
        }
      ],
      "order": 1,
      "createdAt": "2026-10-27T15:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 0,
  "pageCount": 1
}
POST/v1/fields

Declare an app field

Only with an external app's installation token: the field is created in that app's app--<handle> namespace. An academy API key gets 403; the academy defines its own fields in Admin > Custom fields. Requires the fields:write scope.

Scope:fields:write

Body

  • entitystringrequired

    One of: course, bootcamp, path

  • keystringrequired
  • typestringrequired

    One of: text, number, boolean, select, url, date

  • labelobjectrequired
  • optionsobject[]optional
    • valuestringrequired
    • labelstringrequired
  • orderintegeroptional

Responses

  • 201The new definition.
  • 400Invalid body (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 409A field with that key already exists for that entity.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl -X POST https://wislab.app/api/v1/fields \
  -H "Authorization: Bearer $WISLAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "entity": "course",
  "key": "level",
  "type": "text",
  "label": {
    "en": "Level",
    "es": "Nivel"
  },
  "options": [
    {
      "value": "string",
      "label": "string"
    }
  ],
  "order": 1
}'
Response 201
{
  "data": {
    "id": "clx8k2m0p0001",
    "entity": "course",
    "namespace": "academy",
    "key": "level",
    "fieldId": "academy:level",
    "type": "text",
    "label": {
      "en": "Level",
      "es": "Nivel"
    },
    "options": [
      {
        "value": "string",
        "label": "string"
      }
    ],
    "order": 1,
    "createdAt": "2026-10-27T15:00:00.000Z"
  }
}
GET/v1/courses/{id}/fields

Get a course's custom field values

Returns fields keyed namespace:key. Requires the fields:read scope.

Scope:fields:read

Path parameters

  • idstringrequired

    The ID.

Responses

  • 200The values.
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 404No course with that ID in this academy.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/courses/clx8k2m0p0001/fields \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": {
    "id": "clx8k2m0p0001",
    "entity": "course",
    "fields": {
      "academy:level": "intermediate"
    }
  }
}
PATCH/v1/courses/{id}/fields

Update a course's custom field values

An academy API key writes only the academy namespace; an app installation token writes only its own app--<handle> (otherwise 403 NAMESPACE_FORBIDDEN). null clears a value; a field with no definition returns 400 UNKNOWN_FIELD. Requires the fields:write scope.

Scope:fields:write

Path parameters

  • idstringrequired

    The ID.

Body

  • fieldsobjectrequired

Responses

  • 200The values after the change.
  • 400Invalid body, unknown field or wrong value type (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 404No course with that ID in this academy.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl -X PATCH https://wislab.app/api/v1/courses/clx8k2m0p0001/fields \
  -H "Authorization: Bearer $WISLAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "fields": {
    "academy:level": "intermediate"
  }
}'
Response 200
{
  "data": {
    "id": "clx8k2m0p0001",
    "entity": "course",
    "fields": {
      "academy:level": "intermediate"
    }
  }
}
GET/v1/bootcamps/{id}/fields

Get a bootcamp's custom field values

Returns fields keyed namespace:key. Requires the fields:read scope.

Scope:fields:read

Path parameters

  • idstringrequired

    The ID.

Responses

  • 200The values.
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 404No bootcamp with that ID in this academy.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/bootcamps/clx8k2m0p0001/fields \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": {
    "id": "clx8k2m0p0001",
    "entity": "course",
    "fields": {
      "academy:level": "intermediate"
    }
  }
}
PATCH/v1/bootcamps/{id}/fields

Update a bootcamp's custom field values

An academy API key writes only the academy namespace; an app installation token writes only its own app--<handle> (otherwise 403 NAMESPACE_FORBIDDEN). null clears a value; a field with no definition returns 400 UNKNOWN_FIELD. Requires the fields:write scope.

Scope:fields:write

Path parameters

  • idstringrequired

    The ID.

Body

  • fieldsobjectrequired

Responses

  • 200The values after the change.
  • 400Invalid body, unknown field or wrong value type (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 404No bootcamp with that ID in this academy.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl -X PATCH https://wislab.app/api/v1/bootcamps/clx8k2m0p0001/fields \
  -H "Authorization: Bearer $WISLAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "fields": {
    "academy:level": "intermediate"
  }
}'
Response 200
{
  "data": {
    "id": "clx8k2m0p0001",
    "entity": "course",
    "fields": {
      "academy:level": "intermediate"
    }
  }
}
GET/v1/paths/{id}/fields

Get a path's custom field values

Returns fields keyed namespace:key. Requires the fields:read scope.

Scope:fields:read

Path parameters

  • idstringrequired

    The ID.

Responses

  • 200The values.
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 404No path with that ID in this academy.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl https://wislab.app/api/v1/paths/clx8k2m0p0001/fields \
  -H "Authorization: Bearer $WISLAB_API_KEY"
Response 200
{
  "data": {
    "id": "clx8k2m0p0001",
    "entity": "course",
    "fields": {
      "academy:level": "intermediate"
    }
  }
}
PATCH/v1/paths/{id}/fields

Update a path's custom field values

An academy API key writes only the academy namespace; an app installation token writes only its own app--<handle> (otherwise 403 NAMESPACE_FORBIDDEN). null clears a value; a field with no definition returns 400 UNKNOWN_FIELD. Requires the fields:write scope.

Scope:fields:write

Path parameters

  • idstringrequired

    The ID.

Body

  • fieldsobjectrequired

Responses

  • 200The values after the change.
  • 400Invalid body, unknown field or wrong value type (see details).
  • 401Missing, malformed or revoked API key.
  • 403The key lacks the required scope, or the academy's plan doesn't include the API.
  • 404No path with that ID in this academy.
  • 429Rate limit exceeded. Wait Retry-After seconds.
curl -X PATCH https://wislab.app/api/v1/paths/clx8k2m0p0001/fields \
  -H "Authorization: Bearer $WISLAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "fields": {
    "academy:level": "intermediate"
  }
}'
Response 200
{
  "data": {
    "id": "clx8k2m0p0001",
    "entity": "course",
    "fields": {
      "academy:level": "intermediate"
    }
  }
}

Missing an endpoint?

Tell us what you want to integrate and we'll look at it with you.

Contact us