Planaday Public API

Welcome to the Planaday Public API documentation. This API allows you to integrate course booking, student management, and scheduling functionality from Planaday into your own website, portal, or back-office system.

Overview

Last updated

See the changelist at the end of this documentation for a full history of changes.

API URL

Replace apitest in the example URLs with your own company code. Your company code is the same prefix used in your Planaday environment URL.

Example: if your Planaday URL is https://mycompany.planaday.nl, then your API base URL is:

https://mycompany.api.planaday.nl/v1

Example request:

GET https://mycompany.api.planaday.nl/v1/course/list?start=20260101&end=20260331

Notational Conventions

The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC2119.

Authentication

All API requests require authentication via an API key. API keys are managed in the Planaday application under Beheer → API keys. See the support documentation for setup instructions: Handleiding Publieke API.

The API key MUST be provided in the X-Api-Key header on every request:

X-Api-Key: 5113F6314DC7A397323EA383B0D65B39CD03FC9F77F30CDA6E0C9523543DF8B8

Requests without a valid API key will receive a 401 Unauthorized response.

Consumer Identification

The API uses the User-Agent header to identify your application. Always include a descriptive User-Agent so we can identify your integration in our logs when troubleshooting.

Example:

User-Agent: MyBookingWebsite/2.1 (https://www.example.nl)

HTTP Methods

This API uses standard HTTP methods:

Method Purpose Description
GET Read Retrieve a resource or list of resources. Never modifies data.
POST Create Create a new resource, or perform an action (e.g. create a booking, mark a voucher as used).
PUT Replace Replace an entire resource with the provided data.
PATCH Update Partially update a resource — only the provided fields are changed.
DELETE Delete Remove a resource (e.g. cancel a booking).

Media Type

All requests and responses use JSON. Set the following headers on every request:

Content-Type: application/json
Accept: application/json

Omitting these headers may result in a 415 Unsupported Media Type error.

Representation of Date and Time

All dates and times use the Europe/Amsterdam timezone.

Type Format Example
Date YYYYMMDD 20260315
Time HH:mm 09:00
Date (newer endpoints) YYYY-MM-DD 2026-03-15

Names in API vs Planaday

Planaday’s user interface is in Dutch. The table below maps API resource names to their Dutch equivalents in the application, so you can find the right section in Planaday when configuring or troubleshooting your integration.

API name Planaday (Dutch) Description
Booking Aanvraag A course enrollment request from the API
Company Bedrijf Organization that enrolls students
Course Cursus A scheduled course instance with dates
CourseTemplate Cursussjabloon (beheer) Blueprint that defines the course structure
Daypart Dagdeel A single session within a course
Education Opleiding Training program grouping multiple courses
Extrafields Extra velden (beheer) Custom fields defined per entity type
Image Afbeelding Photo or logo attached to a course
Instructor Instructeur (beheer) Trainer or teacher
Label Label Tag for categorizing courses and dayparts
Location Locatie (beheer) Venue where a session takes place
Material Materiaal (beheer) Physical item managed in Planaday
Student Cursist / Medewerker Person enrolled in courses
Voucher Voucher Discount code for courses

Status Codes and Errors

The API uses standard HTTP status codes. Every response includes one of these codes to indicate the result of your request.

Success codes:

Code Status Description
200 OK Request succeeded. Response body contains the requested data.
201 Created Resource was successfully created (response to a POST).
204 No Content Request succeeded but there is no response body (e.g. after a DELETE).

Client error codes:

Code Status Description
400 Bad Request The request is malformed or contains validation errors. Check the response body for details.
401 Unauthorized Missing or invalid API key. Verify your X-Api-Key header.
403 Forbidden Authentication succeeded but your API key does not have access to this resource.
404 Not Found The requested resource does not exist. Verify the ID or URL.
405 Method Not Allowed The HTTP method is not supported for this endpoint (e.g. PUT on a GET-only resource).
406 Not Acceptable The server cannot produce a response matching the Accept header.
409 Conflict The request conflicts with existing data (e.g. duplicate external_id).
415 Unsupported Media Type Missing or wrong Content-Type header. Use application/json.

Server error codes:

Code Status Description
500 Server Error An unexpected error occurred on the server. Contact support if this persists.
503 Rate Limit Reached You have exceeded the rate limit. Wait and retry — see the Ratelimiting section.

Note: Some endpoints also return a custom error code in the response body (e.g. "error": 402). These are internal application codes, not HTTP status codes. Refer to the error tables in each endpoint’s documentation for their meaning.

Ratelimiting

The API enforces rate limits to ensure fair usage. Every response includes headers that tell you your current rate limit status:

X-RateLimit-Limit: 720000
X-RateLimit-Remaining: 719999
X-RateLimit-Reset: 1610536876
Header Description
X-RateLimit-Limit Maximum number of requests allowed per hour
X-RateLimit-Remaining Number of requests remaining in the current window
X-RateLimit-Reset Unix timestamp (seconds) when the rate limit resets

When you exceed the limit, the API returns a 503 Rate Limit Reached response. Wait until the X-RateLimit-Reset timestamp before retrying.

Best practices:

  • Monitor the X-RateLimit-Remaining header to avoid hitting the limit

  • Use batch endpoints (e.g. ?batch=) where available to reduce the number of calls

  • Cache responses that don’t change frequently (e.g. course templates, labels, locations)

Pagination

List endpoints return paginated results. The response includes two extra blocks — meta for pagination info and links for navigation URLs.

Meta block

The meta block tells you where you are in the result set and how many results exist:

{
    "meta": {
        "page": {
            "current": 1,
            "offset": 1,
            "limit": 100,
            "total": 4
        },
        "records": 393
    }
}
Field Description
current Current page number (based on offset and limit)
offset Starting record position
limit Maximum number of records returned per request
total Total number of pages available
records Total number of records matching your query

Example: paging through results

To fetch records 201-300, set ?offset=200&limit=100:

{
    "meta": {
        "page": {
            "current": 3,
            "offset": 200,
            "limit": 100,
            "total": 4
        },
        "records": 393
    }
}

Links block

The links block provides ready-to-use URLs for navigating between pages. Empty strings indicate no further pages in that direction.

{
    "links": {
        "self": "https://apitest.api.planaday.nl/v1/course/1/dayparts?limit=20&offset=20",
        "first": "https://apitest.api.planaday.nl/v1/course/1/dayparts?limit=20&offset=1",
        "last": "https://apitest.api.planaday.nl/v1/course/1/dayparts?limit=20&offset=40",
        "next": "https://apitest.api.planaday.nl/v1/course/1/dayparts?limit=20&offset=40",
        "previous": "https://apitest.api.planaday.nl/v1/course/1/dayparts?limit=20&offset=1"
    }
}
Field Description
self URL for the current page
first URL for the first page
last URL for the last page
next URL for the next page (empty string if on the last page)
previous URL for the previous page (empty string if on the first page)

VAT codes

Cost fields in the API include a vat (percentage) and vat_code (category). The following VAT codes are used throughout the API:

Code Rate Description
0 0% No VAT (vrijgesteld) — e.g. education exempt from VAT
1 9% Low VAT rate (laag tarief) — e.g. certain educational materials
2 21% High VAT rate (hoog tarief) — e.g. commercial training courses

These codes appear in cost blocks for courses, dayparts, course templates, locations, materials, and vouchers.

Attribute blocks

Notice: attribute blocks can only be used when a Planaday employee has enabled this feature for your account.

Attributes are configurable options attached to course templates, courses, and dayparts. They allow customers to make selections during booking — for example, choosing a course language, selecting exam options, or picking equipment variants. When attributes have prices, they can affect the total booking cost.

Attribute blocks appear in the responses of:

  • Course templates (GET /coursetemplate/{id})

  • Courses (GET /course/{id} and GET /course/list)

  • Dayparts within courses (GET /course/{id}/dayparts and GET /daypart/{id})

Structure

Each attribute has a unique code (GUID), a list of possible values, and flags indicating whether the attribute is required, allows multiple selections, or has financial impact:

{
    "Take exam": {
        "code": "{E27A0D46-AAF1-BDB8-A667-D3C0510F0A5E}",
        "values": {
            "ja": {
                "value": "50",
                "description": "display tekst"
            },
            "nee": {
                "value": "0",
                "description": "display tekst"
            }
        },
        "is_required": false,
        "multiple_values_allowed": false,
        "is_financial": false
    }
}

Fields explained

code — The unique identifier (GUID) for this attribute. Use this code when posting attribute selections in the booking call.

values — The possible options for this attribute. Each option is keyed by its name and has a value (price component or identifier) and description (display text). Four structures are possible:

Scenario Example
Both value and description known "ja": { "value": "50", "description": "Incl. examen" }
Only value known "ja": { "value": "50", "description": null }
Only description known "ja": { "value": null, "description": "Incl. examen" }
Neither known "ja": {}

is_required — When true, this attribute must be included in the booking POST. Bookings without required attributes will be rejected with error code 424.

multiple_values_allowed — When true, the customer can select multiple options. Pass them as an array of values in the booking call. When false, only one value is allowed.

is_financial — When true, the attribute affects the booking price. The vat and vat_code fields will be included alongside the values:

{
    "Equipment": {
        "code": "{E27A0D46-AAF1-BDB8-A667-D3C0510F0A5E}",
        "values": {
            "EPT": {
                "value": null,
                "description": "Elektrische pallettruck"
            },
            "Heftruck": {
                "value": "25",
                "description": "Heftruck opleiding"
            },
            "Reachtruck": {
                "value": "50",
                "description": null
            }
        },
        "is_required": false,
        "multiple_values_allowed": false,
        "is_financial": true,
        "vat": 21,
        "vat_code": 2
    }
}

See the Booking documentation for examples of how to post attribute selections.

Booking

Create, manage, and cancel course bookings (enrollments) for students. This is the core integration endpoint — use it to let students book courses from your website, portal, or external system. Bookings appear in Planaday under “Cursus → Openstaande aanvragen” where they can be reviewed and processed by staff.

Booking

POST https://apitest.api.planaday.nl/v1/booking/course_id
RequestsCreate booking for 2 studentCreate booking for a student with extra fieldsCreate booking for a student with attributes
Headers
Content-Type: application/json
X-Api-Key: <apikey>
Body
{
  "students": [
    {
      "id": 12312312,
      "gender": "m",
      "initials": "P.W.M.",
      "first_name": "Pieter",
      "nick_name": "Piet",
      "prefix": "van der",
      "last_name": "Pietersen",
      "email": "pieter@pietersen.nl",
      "address": "Straat",
      "house_number": "1",
      "house_number_extension": null,
      "postal_code": "2132PS",
      "city": "Hoofddorp",
      "country": "Nederland",
      "phonenumber": null,
      "date_of_birth": "20000101",
      "hometown": "maastricht",
      "country_of_birth": "Nederland",
      "employeeId": "ABR1234",
      "costcentercode": "700123",
      "company_position": "Scrum leader",
      "is_contact_person": true,
      "is_student": false,
      "internal_reference": "123445AA",
      "process_code95": false,
      "process_soob": false,
      "language": "NL",
      "stap_regulation": true
    },
    {
      "id": 12312313,
      "gender": "f",
      "initials": "K.S",
      "first_name": "Klara",
      "last_name": "Pietersen",
      "maiden_name": "Delft",
      "email": "klara@pietersen.nl",
      "address": "Straat",
      "house_number": "1",
      "house_number_extension": null,
      "postal_code": "2132PS",
      "city": "Hoofddorp",
      "phonenumber": null,
      "date_of_birth": null,
      "employeeId": "ABR1235",
      "costcentercode": "700123",
      "company_position": "Scrum leader",
      "is_contact_person": false,
      "is_student": true,
      "internal_reference": "123445AA",
      "remark": "This is a remark",
      "stap_regulation": false
    }
  ],
  "company": {
    "name": "Pietersen BV",
    "email": "klara@pietersen.nl",
    "address": "Straat",
    "house_number": "1",
    "house_number_extension": null,
    "postal_code": "2132PS",
    "city": "Hoofddorp",
    "phonenumber": null,
    "invoice_email": "facturatie@pietersen.nl",
    "Chamber_of_commerce": "AS1231231a",
    "invoice": {
      "address": "Paalbergweg",
      "house_number": "134",
      "house_number_extension": null,
      "postal_code": "1111AA",
      "city": "Amsterdam",
      "country": "Nederland"
    },
    "mailing": {
      "address": "Paalbergweg",
      "house_number": "133",
      "house_number_extension": "Zw",
      "postal_code": "1111AA",
      "city": "Amsterdam",
      "country": "Nederland"
    }
  },
  "created_at": "2017-12-17T22:50:27+00:00",
  "creating_source": "www.testsite.nl",
  "course_id": 2,
  "education_id": null,
  "dayparts": [
    76,
    77,
    78,
    79
  ],
  "materials": [
    405,
    407
  ],
  "label": "Group1",
  "is_payed": true,
  "payment": {
    "party": "ideal",
    "transaction_id": "1231ASSBBD12321"
  }
}
Responses200400400404
Headers
Content-Type: application/json
Body
{
  "result": "OK",
  "booking_id": "API5ce597c40e0fc6.85265205",
  "details": {
    "company": {
      "action": "reused",
      "id": 52,
      "name": "Pietersen BV"
    },
    "students": [
      {
        "action": "reused",
        "type": "employee",
        "id": 12312312,
        "name": "Pietersen, Pieter",
        "option_ids": [
          65,
          66,
          67,
          68
        ]
      },
      {
        "action": "reused",
        "type": "employee",
        "id": 12312313,
        "name": "Pietersen, Klara",
        "option_ids": [
          69,
          70,
          71,
          72
        ]
      }
    ]
  }
}
Headers
Content-Type: application/json
Body
{
  "error": 402,
  "message": "Missing students block"
}
Headers
Content-Type: application/json
Body
{
  "error": 425,
  "message": "attribute: unknown value given for attribute {1A811192-969F-323B-04C7-257C7BC6C897} : foo. Allowed: Reachtruck/ EPT"
}
Headers
Content-Type: application/json
Body
{
  "message": "Unavailable course selected"
}
Headers
Content-Type: application/json
X-Api-Key: <apikey>
Body
{
  "students": [
    {
      "id": 12312312,
      "gender": "m",
      "initials": "P.W.M.",
      "first_name": "Pieter",
      "last_name": "Pietersen",
      "email": "pieter@pietersen.nl",
      "address": "Straat",
      "house_number": "1",
      "house_number_extension": null,
      "postal_code": "2132PS",
      "city": "Hoofddorp",
      "country": "Nederland",
      "phonenumber": null,
      "date_of_birth": "20000101",
      "hometown": "maastricht",
      "country_of_birth": "Nederland",
      "employeeId": "ABR1234",
      "costcentercode": "700123",
      "company_position": "Scrum leader",
      "is_contact_person": true,
      "is_student": false,
      "internal_reference": "123445AA",
      "extrafields": {
        "49": "15",
        "51": "bhv|brand|ehbo",
        "52": "false",
        "201": "true"
      },
      "process_code95": false,
      "process_soob": false,
      "language": "NL"
    }
  ],
  "company": {
    "name": "Pietersen BV",
    "email": "klara@pietersen.nl",
    "address": "Straat",
    "house_number": "1",
    "house_number_extension": null,
    "postal_code": "2132PS",
    "city": "Hoofddorp",
    "phonenumber": null,
    "invoice_email": "facturatie@pietersen.nl"
  },
  "created_at": "2017-12-17T22:50:27+00:00",
  "creating_source": "www.testsite.nl",
  "course_id": 2,
  "education_id": 12,
  "dayparts": [
    76,
    77,
    78,
    79
  ]
}
Responses200400400404
Headers
Content-Type: application/json
Body
{
  "result": "OK",
  "booking_id": "API5ce597c40e0fc6.85265205",
  "details": {
    "company": {
      "action": "reused",
      "id": 52,
      "name": "Pietersen BV"
    },
    "students": [
      {
        "action": "reused",
        "type": "employee",
        "id": 12312312,
        "name": "Pietersen, Pieter",
        "option_ids": [
          65,
          66,
          67,
          68
        ]
      },
      {
        "action": "reused",
        "type": "employee",
        "id": 12312313,
        "name": "Pietersen, Klara",
        "option_ids": [
          69,
          70,
          71,
          72
        ]
      }
    ]
  }
}
Headers
Content-Type: application/json
Body
{
  "error": 402,
  "message": "Missing students block"
}
Headers
Content-Type: application/json
Body
{
  "error": 425,
  "message": "attribute: unknown value given for attribute {1A811192-969F-323B-04C7-257C7BC6C897} : foo. Allowed: Reachtruck/ EPT"
}
Headers
Content-Type: application/json
Body
{
  "message": "Unavailable course selected"
}
Headers
Content-Type: application/json
X-Api-Key: <apikey>
Body
{
  "students": [
    {
      "id": 12312313,
      "gender": "f",
      "initials": "K.S",
      "first_name": "Klara",
      "last_name": "Pietersen",
      "email": "klara@pietersen.nl",
      "address": "Straat",
      "house_number": "1",
      "house_number_extension": null,
      "postal_code": "2132PS",
      "city": "Hoofddorp",
      "phonenumber": null,
      "date_of_birth": null,
      "employeeId": "ABR1235",
      "costcentercode": "700123",
      "company_position": "Scrum leader",
      "is_contact_person": false,
      "is_student": true,
      "internal_reference": "123445AA",
      "remark": "This is a remark"
    }
  ],
  "company": {
    "name": "Pietersen BV",
    "email": "klara@pietersen.nl",
    "address": "Straat",
    "house_number": "1",
    "house_number_extension": null,
    "postal_code": "2132PS",
    "city": "Hoofddorp",
    "phonenumber": null,
    "invoice_email": "facturatie@pietersen.nl"
  },
  "created_at": "2021-05-01T22:50:27+00:00",
  "creating_source": "www.testsite.nl",
  "course_id": 2,
  "education_id": 12,
  "dayparts": [
    76,
    77
  ],
  "attributes": [
    {
      "code": "{E27A0D46-AAF1-BDB8-A667-D3C0510F0A5EF}",
      "values": [
        "foo",
        "bar"
      ]
    }
  ]
}
Responses200400400404
Headers
Content-Type: application/json
Body
{
  "result": "OK",
  "booking_id": "API5ce597c40e0fc6.85265205",
  "details": {
    "company": {
      "action": "reused",
      "id": 52,
      "name": "Pietersen BV"
    },
    "students": [
      {
        "action": "reused",
        "type": "employee",
        "id": 12312312,
        "name": "Pietersen, Pieter",
        "option_ids": [
          65,
          66,
          67,
          68
        ]
      },
      {
        "action": "reused",
        "type": "employee",
        "id": 12312313,
        "name": "Pietersen, Klara",
        "option_ids": [
          69,
          70,
          71,
          72
        ]
      }
    ]
  }
}
Headers
Content-Type: application/json
Body
{
  "error": 402,
  "message": "Missing students block"
}
Headers
Content-Type: application/json
Body
{
  "error": 425,
  "message": "attribute: unknown value given for attribute {1A811192-969F-323B-04C7-257C7BC6C897} : foo. Allowed: Reachtruck/ EPT"
}
Headers
Content-Type: application/json
Body
{
  "message": "Unavailable course selected"
}

Create booking
POST/booking/{course_id}

Create a booking for one or more students, optionally linked to a company. The booking appears in Planaday under “Cursus → Openstaande aanvragen” for staff to review and process.

Common scenarios:

  • A student books a course from your website (1 student, with or without company)

  • An HR manager enrolls multiple employees at once (multiple students + company block)

  • A booking platform submits a paid enrollment (with is_payed and payment block)

Student block

The students block is required and can contain one or more student objects.

The complete list of fields for each student object is listed in the request example. The following fields are required:

  • last_name

  • email (can be null)

  • phonenumber (can be null)

Possible gender values are:

  • ‘m’ (male)

  • ‘f’ (female)

  • ‘nvt’ (inapplicable) or ‘nr’ (not relevant)

  • ‘null’ value or ‘u’ (unkown)

  • no gender field = ‘u’

The language field is optional, but these are the possible language values:

  • ‘NL’ (dutch)

  • ‘ENG’ (english)

  • ‘FR’ (French)

  • ‘DU’ (German)

  • ‘PL’ (Polish)

  • No language field = ‘NL’

When an id is specified we will link the booking to this user known in Planaday. This field is optional.

The optional costcentercode field must contain a known code you defined in Planaday (beheer -> kostenplaats).

The optional fields block can contain extra/custom fields as defined in the Planaday application. These are always in the format: "fieldname": "value". (Deprecated since 2023-01-11 — use extrafields instead)

The optional extrafields block can contain extra fields as defined in the Planaday application. These are always in the format: "fieldid": "value(s)". See the /extrafields/list call for a list of available extra fields.

{
  "extrafields": {
    "50": "invoice@example.com",
    "51": "Financial person",
    "52": "950123"
  }
}

When multiple values are possible, separate them with a | (pipe character):

{
  "extrafields": {
    "53": "bhv|brand|ehbo"
  }
}

You can create contacts for a company by using the is_contact and is_student fields. When you make ‘is_contact’ true and ‘is_student’ false, only a contact will be added for the company.

You can add an optional internal reference to a student, which will be copied from the booking to the real student. This field is called internal_reference.

It is always possible to update student information using the /student call (use the employeeId in the external_id!)

Company id

If you want to preselect a company for the booking, you can add the company_id field. This is useful when creating bookings for a specific company. This field is optional.

Note: you can use either company_id or the company block, but not both.

Company block

The whole company block is optional. When it’s added the following fields are required:

  • name

  • address

  • house_number

  • postal_code

  • city

Within the company block there are 2 optional sub-blocks. All fields within these sub-blocks are optional.

Invoice information block

{
  "invoice": {
    "email": "invoice@example.com",
    "addressee": "Financial person",
    "address": "Paalbergweg",
    "house_number": "134",
    "house_number_extension": null,
    "postal_code": "1111AA",
    "city": "Amsterdam",
    "country": "Nederland"
  }
}

and mailing address information block

{
  "mailing": {
    "address": "Paalbergweg",
    "house_number": "134",
    "house_number_extension": null,
    "postal_code": "1111AA",
    "city": "Amsterdam",
    "country": "Nederland"
  }
}

When no company block is added, each student will be added as a private (particulier) student.

Dayparts block

The dayparts block is an optional list of dayparts ids that will be booked for the students. If the ‘dayparts’ block is an empty list then the students will be booked for all dayparts of the selected course.

Payment block

If the is_payed field is added and has the true value, the payment block is required!

This must contain the following fields

  • party

  • transaction_id

Attributes block

Notice: This block can only be used when a Planaday employee gives the rights to use this!

When dayparts or courses have required attributes, the attributes block in the POST is also required. The booking API gives as much information as possible when there are errors in the post.

When attributes are required for a course/daypart, it is not possible to send multiple students in the booking.

Attributes block always contains the following:

{
  "attributes": [
    {
      "code": "{E27A0D46-AAF1-BDB8-A667-D3C0510F0A5EF}",
      "values": [
        "foo"
      ]
    }
  ]
}

and when multiple items (in this case items with values) are allowed the value can contain:

{
  "attributes": [
    {
      "code": "{E27A0D46-AAF1-BDB8-A667-D3C0510F0A5E}",
      "values": [
        "Foo",
        "Bar"
      ]
    }
  ]
}

General items

You can add an additional creating_source string which you can use to determine the source of the API request. (i.e. the url of the website which uses the API) This string will be visible in the Planaday application.

It is possible to label a booking with the optional label field. This field may contain a-z, A-Z, 0-9 and - (dash). No spaces or other characters are allowed.

Error codes

Code Message
400 Bad Request, 400: parameters missing: id
400 Bad Request, 401: unable to parse posted json
400 Bad Request, 402: missing students block
400 Bad Request, 403: students:gender missing, empty or invalid value (m/f/nvt/u/nr allowed)
400 Bad Request, 404: students:first_name missing or empty
400 Bad Request, 405: students:last_name missing or empty
400 Bad Request, 406: students:email contains invalid value ()
400 Bad Request, 407: course_id missing or not the same as url parameter
400 Bad Request, 408: company:name missing or empty
400 Bad Request, 409: company:address missing or empty
400 Bad Request, 410: company:house_number missing or empty
400 Bad Request, 411: company:postal_code missing or empty
400 Bad Request, 412: company:city missing or empty
400 Bad Request, 413: course selected without dayparts
400 Bad Request, 414: unknown course selected
400 Bad Request, 415: course selected without dayparts
400 Bad Request, 416: dayparts includes id not present in given course
400 Bad Request, 417: material includes id not present in given course
400 Bad Request, 418: payment block is missing
400 Bad Request, 419: attribute:attributes block is missing
400 Bad Request, 420: attribute:only 1 student allowed when attributes block is set
400 Bad Request, 421: attribute:code is missing
400 Bad Request, 422: attribute:value is missing
400 Bad Request, 423: attribute:unknown attribute code given for this course/daypart: {}. Allowed codes: [list]
400 Bad Request, 424: attribute:not all required attributes are posted. Missing attributes: [list]
400 Bad Request, 425: attribute:unkown value given for attribute {}: [value], Allowed values: [list]
400 Bad Request, 426: students:unknown language given (), allowed: [list]
400 Bad Request, 427: attribute:Attributes given, but none required/configured
400 Bad Request, 428: attribute:Multiple item given for attribute {}. Only 1 allowed
400 Bad Request, 429: attribute:Value required, but non given
400 Bad Request, 430: students:Bad date of birth given
400 Bad Request, 431: students:date of birth is required for STAP courses
400 Bad Request, 432: students:address is required for STAP courses
400 Bad Request, 433: students:house_number is required for STAP courses
400 Bad Request, 434: students:postal_code is required for STAP courses
400 Bad Request, 435: students:city is required for STAP courses
400 Bad Request, 436: Non STAP booking is not allowed on STAP only course
400 Bad Request, 437: Unkown company_id given
401 No Access Right
404 Course not found
URI Parameters
HideShow
course_id
number (required) 

ID of the Course to create booking for in the form of an integer


Booking payed

POST https://apitest.api.planaday.nl/v1/booking/payed/booking_id
RequestsMark booking as payed
Headers
Content-Type: application/json
X-Api-Key: <apikey>
Body
{
  "is_payed": true,
  "payment": {
    "party": "ideal",
    "transaction_id": "1231ASSBBD12321"
  }
}
Responses200400404
Headers
Content-Type: application/json
Body
{
  "result": "OK",
  "bookingId": "API5ce597c40e0fc6.85265205"
}
Headers
Content-Type: application/json
Body
{
  "error": 401,
  "message": "Parameters missing: id"
}
Headers
Content-Type: application/json
Body
{
  "message": "Unavailable booking selected"
}

Mark booking as payed
POST/booking/payed/{booking_id}

Use this call to mark a booking as paid. This is useful after handling a payment callback.

If the ‘is_payed’ field is ‘true’, the ‘payment’ block is required!

Error codes

Code Message
400 Bad Request, 400: Parameters missing: booking_id
400 Bad Request, 401: Unable to parse posted json
400 Bad Request, 402: Missing payment block
400 Bad Request, 403: Missing party
400 Bad Request, 404: Missing transaction_id
404 Booking not found
URI Parameters
HideShow
booking_id
string (required) 

ID of the booking received in the booking post call (i.e API5ce597c40e0fc6.85265205)


Delete booking

DELETE https://apitest.api.planaday.nl/v1/booking/booking_id
RequestsDelete bookingDelete booking using internal reference
Headers
Content-Type: application/json
X-Api-Key: <apikey>
Responses200400404
Headers
Content-Type: application/json
Body
{
  "result": "OK",
  "bookingId": "API5ce597c40e0fc6.85265205"
}
Headers
Content-Type: application/json
Body
{
  "error": 401,
  "message": "Parameters missing: bookingid"
}
Headers
Content-Type: application/json
Body
{
  "message": "Booking not found"
}
Headers
Content-Type: application/json
X-Api-Key: <apikey>
Body
{
  "internal_reference": "1234AA123",
  "deleting_source": "plusport",
  "reason": "Student is ill"
}
Responses200400404
Headers
Content-Type: application/json
Body
{
  "result": "OK",
  "bookingId": "API5ce597c40e0fc6.85265205"
}
Headers
Content-Type: application/json
Body
{
  "error": 401,
  "message": "We found some errors, please check",
  "errors": "No or invalid cancel information given"
}
Headers
Content-Type: application/json
Body
{
  "message": "Booking not found"
}

Delete made booking
DELETE/booking/{booking_id}

Cancel a previously created booking. Use this when a student cancels their enrollment, or when an external booking platform sends a cancellation.

First we try with the {booking_id}. This works when the booking hasn’t been converted to a planned student yet. When the booking is already converted, a cancel request will be created in the backoffice.

When the booking_id isn’t found, a second attempt is made using the 3 fields below.

When no booking is found at all, an error is returned.

When provided, the reason field will be added to the notes.

Error codes

Code Message
400 Bad Request, 400: Parameters missing: bookingid
400 Bad Request, 401: Unkown bookingid and no or invalid cancel information given
400 Bad Request, 402: Missing or empty internal_reference
400 Bad Request, 403: Missing or empty deleting_source
400 Bad Request, 404: Missing or empty reason
404 Booking not found
URI Parameters
HideShow
booking_id
string (required) 

ID of the booking received in the booking post call (i.e API5ce597c40e0fc6.85265205)


Company

Manage companies (bedrijven) in Planaday. Companies are organizations that enroll students for courses. They can be organized in a parent-child hierarchy (e.g. holding company → subsidiaries). Use these endpoints to synchronize company data from an external HR or CRM system.

Company list

GET https://apitest.api.planaday.nl/v1/company/list
Requestsexample 1
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "meta": {
    "page": {
      "current": 2,
      "offset": 1,
      "limit": 1,
      "total": 10153
    },
    "records": 10153
  },
  "data": [
    {
      "id": 1,
      "code": "PAD1234",
      "name": "Planaday B.V.",
      "address": {
        "street": "Opaallaan",
        "housenumber": "1180",
        "housenumber_extension": null,
        "zipcode": "2132LN",
        "city": "Hoofddorp",
        "country": "Nederland"
      },
      "contact": {
        "phonenumber": "023-1234567",
        "emailaddress": "test@test.com"
      },
      "external_id": "a123D",
      "parent_company_id": "123",
      "has_childs": false,
      "students": {
        "active": 10,
        "inactive": 3
      }
    }
  ],
  "links": {
    "self": "https://apitest.api.planaday.nl/v1/company/list?offset=1",
    "first": "https://apitest.api.planaday.nl/v1/company/list?offset=1",
    "last": "https://apitest.api.planaday.nl/v1/company/list?offset=10",
    "previous": "",
    "next": "https://apitest.api.planaday.nl/v1/company/list?offset=10"
  }
}

Get a list of companies
GET/company/list

Retrieve a paginated list of all active (non-private) companies. Use this to synchronize company data to your system, or to populate a company selection dropdown when creating bookings.

The parent_company_id links to the parent company. The has_childs field indicates if the company has child companies. The external_id is the reference to the external system.


Company

POST https://apitest.api.planaday.nl/v1/company
RequestsCreate new companyUpdate company: connect to other parent company
Headers
X-Api-Key: <apikey>
Body
{
  "active": true,
  "name": "Company 1",
  "address": {
    "street": "Logger",
    "housenumber": "123",
    "housenumber_extension": null,
    "zipcode": "1111AA",
    "city": "Amstelveen",
    "country": null
  },
  "contact": {
    "phonenumber": "0612345679",
    "emailaddress": "info@company1.nl"
  },
  "external_id": 112355,
  "parent_company_id": null,
  "remark": "Aanmaak"
}
Responses200200400404409
Headers
Content-Type: application/json
Body
{
  "external_id": 112355,
  "company_id": 40,
  "message": "Company created"
}
Headers
Content-Type: application/json
Body
{
  "external_id": 112355,
  "company_id": 40,
  "message": "Company updated"
}
Headers
Content-Type: application/json
Body
{
  "message": "Missing or invalid value for: [fieldname]"
}
Headers
Content-Type: application/json
Body
{
  "message": "Parent company not found"
}
Headers
Content-Type: application/json
Body
{
  "message": "External id not matching with given company"
}
Headers
X-Api-Key: <apikey>
Body
{
  "active": true,
  "name": "Company 1",
  "address": {
    "street": "Logger",
    "housenumber": "123",
    "housenumber_extension": null,
    "zipcode": "1111AA",
    "city": "Amstelveen",
    "country": null
  },
  "contact": {
    "phonenumber": "0612345679",
    "emailaddress": "info@company1.nl"
  },
  "external_id": 112355,
  "parent_company_id": 12,
  "remark": "Dit bedrijf hangt nu onder ander bedrijf"
}
Responses200200400404409
Headers
Content-Type: application/json
Body
{
  "external_id": 112355,
  "company_id": 40,
  "message": "Company created"
}
Headers
Content-Type: application/json
Body
{
  "external_id": 112355,
  "company_id": 40,
  "message": "Company updated"
}
Headers
Content-Type: application/json
Body
{
  "message": "Missing or invalid value for: [fieldname]"
}
Headers
Content-Type: application/json
Body
{
  "message": "Parent company not found"
}
Headers
Content-Type: application/json
Body
{
  "message": "External id not matching with given company"
}

Create or update a company
POST/company

Create a new company or update an existing one. The external_id field acts as the unique identifier from your system — if a company with this external_id already exists, it will be updated. Every action creates an audit note on the company record.

Typical use cases: syncing company data from an HR system, onboarding a new client, or updating company address after a move.

Required fields

The following fields are required

  • name

  • external_id

Creating

When a parent_company_id is given, the company will placed as a child below the given parent company.

Updating

When posting a company with an existing external_id the data will be updated.

When a parent_company_id is given that is different from the current one, the company will be placed as a child below the new parent company.


Company

POST https://apitest.api.planaday.nl/v1/company/transferstudents
Requestsexample 1
Headers
X-Api-Key: <apikey>
Body
{
  "from_company_id": 2,
  "to_company_id": 9,
  "inactive_also": false
}
Responses200400400400400404404
Headers
Content-Type: application/json
Body
{
  "amount_transfered": 12,
  "message": "Migration done"
}
Headers
Content-Type: application/json
Body
{
  "message": "Parameters missing or empty: from_company_id and/or to_company_id"
}
Headers
Content-Type: application/json
Body
{
  "message": "To and from company can not be the same"
}
Headers
Content-Type: application/json
Body
{
  "message": "To company not allowed"
}
Headers
Content-Type: application/json
Body
{
  "message": "From company not allowed"
}
Headers
Content-Type: application/json
Body
{
  "message": "Company to transfer from not found"
}
Headers
Content-Type: application/json
Body
{
  "message": "Company to transfer to not found"
}

Transfer all students from one company to another
POST/company/transferstudents

Transfer all students from one company to another. Use this when a company is acquired, merged, or restructured. By default only active students are transferred — set inactive_also to true to include inactive students as well. A note is created on each transferred student’s record for audit purposes.

Required fields

The following fields are required

  • from_company_id

  • to_company_id

  • inactive_also (default false)


Course

Courses are scheduled instances of course templates with specific dates, locations, and availability. A course contains one or more dayparts (sessions) and can be part of an education (training program). Use these endpoints to build a course catalog, display available courses on your website, and show detailed course information before booking.

Course list

GET https://apitest.api.planaday.nl/v1/course/list?start=start&end=end&templateid=templateid&offset=offset&limit=limit&label=label
RequestsNormal answerAnswer with extrafields
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "meta": {
    "page": {
      "offset": 0,
      "limit": 30,
      "total": 1
    },
    "records": 2
  },
  "data": [
    {
      "id": 1,
      "code": "d52-201710-001",
      "name": "Gereedschap verkleinen",
      "description": null,
      "type": "open",
      "status": "active",
      "daypart_amount": 2,
      "daypart_amounts": {
        "total": 2,
        "visible": 2,
        "elearning": 1,
        "classroom": 1,
        "connected": 0
      },
      "daypart_display_type": "all_selected_immutable",
      "dayparts": [
        {
          "id": 3,
          "href": "https://apitest.api.planaday.nl/v1/daypart/3",
          "visible": true,
          "status": "plannend",
          "labels": null,
          "is_elearning": false,
          "date": "2024-08-06"
        }
      ],
      "daypart_ids": [
        229,
        230
      ],
      "users": {
        "min": 7,
        "max": 14,
        "available": 11,
        "options": 1
      },
      "costs": {
        "type": "daypart",
        "user": 321,
        "course": 2884,
        "vat": 21,
        "vat_code": 2,
        "remark": null
      },
      "coursetemplate": {
        "id": 123,
        "href": "https://apitest.api.planaday.nl/v1/coursetemplate/123"
      },
      "start_guaranteed": 0,
      "moneyback_guaranteed": 1,
      "has_elearning": 1,
      "has_code95": 1,
      "has_soob": 0,
      "has_stap": 0,
      "stap_only": 0,
      "href": "https://apitest.api.planaday.nl/v1/course/1",
      "labels": [
        "EHBO",
        "BHV"
      ],
      "attributes": {
        "Take exam": {
          "code": "{182BF814-FAA4-1C3C-941A-FCD180A78033}",
          "values": {
            "ja": {
              "value": "50",
              "description": "display tekst"
            },
            "nee": {
              "value": "0",
              "description": "display tekst"
            }
          },
          "is_required": true,
          "multiple_values_allowed": false,
          "is_financial": true,
          "vat": 21,
          "vat_code": 2
        }
      },
      "language": "NL",
      "part_of_education": true,
      "educations": [
        {
          "id": 1,
          "href": "https://apitest.api.planaday.nl/v1/education/1",
          "costs": {
            "user": 1230,
            "vat": 21,
            "vat_code": 2
          }
        },
        {
          "id": 2,
          "href": "https://apitest.api.planaday.nl/v1/education/2",
          "costs": {
            "user": 1295,
            "vat": 21,
            "vat_code": 2
          }
        }
      ],
      "repeating_course": null
    },
    {
      "id": 2,
      "code": "986-201710-003",
      "name": "Fiets slopen",
      "description": null,
      "type": "open",
      "status": "concept",
      "daypart_amount": 1,
      "dayparts": [
        {
          "id": 2,
          "href": "https://apitest.api.planaday.nl/v1/daypart/2",
          "visible": false
        }
      ],
      "users": {
        "min": 5,
        "max": 12,
        "available": 9,
        "options": 2
      },
      "costs": {
        "user": 109,
        "course": 3482,
        "vat": 21,
        "vat_code": 2,
        "remark": null
      },
      "coursetemplate": {
        "id": 124,
        "href": "https://apitest.api.planaday.nl/v1/coursetemplate/124"
      },
      "start_guaranteed": 0,
      "moneyback_guaranteed": 1,
      "has_elearning": 0,
      "has_code95": 0,
      "has_soob": 0,
      "has_stap": 1,
      "stap_only": 1,
      "href": "https://apitest.api.planaday.nl/v1/course/2",
      "labels": [
        "CODE95"
      ],
      "attributes": [],
      "language": "NL",
      "part_of_education": true,
      "educations": [],
      "repeating_course": null
    }
  ],
  "links": {
    "self": "https://apitest.api.planaday.nl/v1/course/list?start=20171101&end=20171101&offset=1",
    "first": "https://apitest.api.planaday.nl/v1/course/list?start=20171101&end=20171101&offset=1",
    "last": "https://apitest.api.planaday.nl/v1/course/list?start=20171101&end=20171101&offset=10",
    "previous": "",
    "next": "https://apitest.api.planaday.nl/v1/course/list?start=20171101&end=20171101&offset=10"
  }
}
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "id": 1,
  "href": "http://apitest.api.planaday.localhost/v1/course/1",
  "code": "96a-202108-001",
  "name": "EAD en reanimatie",
  "description": "",
  "type": "open",
  "status": "concept",
  "daypart_display_type": "none_selected",
  "daypart_amount": 0,
  "daypart_amounts": {
    "total": 0,
    "visible": 0,
    "elearning": 0,
    "classroom": 0,
    "connected": 0
  },
  "dayparts": [],
  "users": {
    "min": 6,
    "max": 15,
    "available": 15,
    "options": 0
  },
  "costs": {
    "type": "course",
    "user": 387,
    "course": 2469,
    "vat": 21,
    "vat_code": 2,
    "remark": ""
  },
  "coursetemplate": {
    "id": 1,
    "href": "http://apitest.api.planaday.localhost/v1/coursetemplate/1"
  },
  "level": "Advanced",
  "level_description": "",
  "start_guaranteed": false,
  "moneyback_guaranteed": false,
  "has_elearning": false,
  "has_code95": false,
  "has_soob": false,
  "has_stap": false,
  "stap_only": false,
  "labels": [],
  "language": "NL",
  "extrafields": {
    "Startdatum": "23-08-2021"
  },
  "repeating_courses": [
    {
      "id": null,
      "href": null
    }
  ]
}

Get a list of courses
GET/course/list?start={start}&end={end}&templateid={templateid}&offset={offset}&limit={limit}&label={label}

Retrieve courses within a date range. This is the primary endpoint for building a course catalog or booking page. Filter by templateid to show only courses of a specific type, or by label to show courses tagged with specific categories.

Results are paginated — use offset and limit to page through large result sets.

Label

The label parameter can contain 1 label to select courses that matches 1 label

&label=BHV

or a list of labels separated by , (comma) to select courses that match ALL labels:

&label=BHV,LOGISTIEK

Daypart_display_type

This field contains the way the dayparts must/can be shown. Possible values are:

  • all_selected: All dayparts must be preselected, but can be unselected

  • none_selected: None of the dayparts are selected, but can be

  • max_one_selected: Only one of all daypart may be selected

  • all_selected_immutable: All day parts are selected and can’t be unselected

Dayparts

Note: this endpoint returns only the first daypart of the course. To get all dayparts, use the Get a course call.

Daypart_ids

This block contains the IDs of all visible dayparts. Use these with the batch call to fetch them all.

Costs

The cost block contains a type, this will contain: daypart (per user) or course.

User is price per student, course is price per course.

Error codes

Code Message
400 Bad Request
401 No Access Right
404 Course not found
URI Parameters
HideShow
start
date (required) 

Start date of planned courses to look for (YYYYMMDD)

end
date (required) 

End date of planned courses to look for (YYYYMMDD)

templateid
number (optional) 

ID of the Coursetemplate to find courses for

offset
number (optional) 

Start offset of courses to show (i.e. 25)

limit
number (optional) 

Amount of courses to show in one response (i.e. 25, limited to 100)

label
string (optional) 

labels which the course MUST have (see also label API calls)


Get Course

GET https://apitest.api.planaday.nl/v1/course/course_id?batch=batchlist
Requestsexample 1with batch
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "id": 8,
  "code": "Test-01/10",
  "name": "Test cursus",
  "description": "Dit is een test cursus",
  "type": "open",
  "status": "active",
  "daypart_display_type": "max_one_selected",
  "daypart_amount": 2,
  "daypart_amounts": {
    "total": 2,
    "visible": 2,
    "elearning": 1,
    "classroom": 1,
    "connected": 0
  },
  "dayparts": [
    {
      "id": 1,
      "href": "https://apitest.api.planaday.nl/v1/daypart/1",
      "visible": true,
      "status": "option",
      "labels": [
        "CODE95"
      ],
      "is_elearning": false,
      "date": "2024-08-06",
      "attributes": {
        "Aanbieding": {
          "code": "{E27A0D46-AAF1-BDB8-A667-D3C0510F0A5E}",
          "values": {
            "ja": {
              "value": "50",
              "description": "display tekst"
            }
          },
          "is_required": false,
          "multiple_values_allowed": false,
          "is_financial": true,
          "vat": 21,
          "vat_code": 2
        }
      }
    },
    {
      "id": 2,
      "href": "https://apitest.api.planaday.nl/v1/daypart/2",
      "labels": [
        "EHBO",
        "BHV"
      ],
      "visible": true,
      "status": "concept"
    }
  ],
  "users": {
    "min": 5,
    "max": 10,
    "available": 7,
    "options": 2
  },
  "costs": {
    "type": "daypart",
    "user": 1299,
    "course": 4500,
    "vat": 21,
    "remark": null
  },
  "coursetemplate": {
    "id": 123,
    "href": "https://apitest.api.planaday.nl/v1/coursetemplate/123"
  },
  "level": "Advanced",
  "level_description": "Higher level course",
  "start_guaranteed": 0,
  "moneyback_guaranteed": 1,
  "has_elearning": 1,
  "has_code95": 1,
  "code95": {
    "practice": 12,
    "theory": 5
  },
  "has_soob": 0,
  "has_stap": false,
  "stap_only": false,
  "href": "https://apitest.api.planaday.nl/v1/course/1",
  "labels": [
    "CODE95"
  ],
  "attributes": {
    "Aanbieding": {
      "code": "{E27A0D46-AAF1-BDB8-A667-D3C0510F0A5E}",
      "values": {
        "ja": {
          "value": "50",
          "description": "display tekst"
        }
      },
      "is_required": false,
      "multiple_values_allowed": false,
      "is_financial": false
    },
    "Taal": {
      "code": "{B299A824-1174-9501-70B0-1F505B50E18F}",
      "values": {
        "nl": {
          "value": null,
          "description": "display tekst"
        },
        "eng": {
          "value": "25",
          "description": "Engels"
        },
        "du": {},
        "pl": {
          "value": "50",
          "description": null
        }
      },
      "is_required": true,
      "multiple_values_allowed": false,
      "is_financial": true,
      "vat": 21,
      "vat_code": 2
    }
  },
  "recurrence": [
    {
      "id": 1,
      "period": 14,
      "unit": "maanden",
      "is_required": true,
      "href": "http://development.api.planaday.localhost/v1/coursetemplate/1"
    },
    {
      "id": 1,
      "period": 23,
      "unit": "maanden",
      "is_required": true,
      "href": "http://development.api.planaday.localhost/v1/coursetemplate/1"
    }
  ],
  "language": "NL",
  "part_of_education": true,
  "educations": [
    {
      "id": 1,
      "href": "https://apitest.api.planaday.nl/v1/education/1",
      "costs": {
        "user": 1230,
        "vat": 21,
        "vat_code": 2
      }
    },
    {
      "id": 2,
      "href": "https://apitest.api.planaday.nl/v1/education/2",
      "costs": {
        "user": 1295,
        "vat": 21,
        "vat_code": 2
      }
    }
  ]
}
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
[
    {
        "id": 8,
        "code": "Test-01/10",
        "name": "Test cursus",
        ...
    },
    {
        "id": 9,
        "code": "Test-01/11",
        "name": "Test cursus 2",
        ...
    }
]

View Course Detail
GET/course/{course_id}?batch={batchlist}

Retrieve full details for a single course, including all dayparts, costs, attributes, recurrence info, and education links. Use this to display a course detail page with the complete schedule, or to gather all information needed before creating a booking.

Use ?batch= to fetch multiple courses in one call — useful when loading a shortlist or comparing courses.

Daypart_display_type

This field contains the way the dayparts must/can be shown. Possible values are:

  • all_selected: All dayparts must be preselected, but can be unselected

  • none_selected: None of the dayparts are selected, but can be

  • max_one_selected: Only one of all daypart may be selected

  • all_selected_immutable: All day parts are selected and can’t be unselected

Costs

The cost block contains a type, this will contain: daypart (per user) or course.

User is price per student, course is price per course.

Error codes

Code Message
401 No Access Right
404 Course not found
URI Parameters
HideShow
course_id
number (required) 

ID of the Course in the form of an integer

batchlist
string (required) 

List of IDs separated by + (plus)


Course dayparts

GET https://apitest.api.planaday.nl/v1/course/course_id/dayparts?offset=offset&limit=limit
Responses200
Headers
Content-Type: application/json
Body
{
  "meta": {
    "page": {
      "offset": 1,
      "limit": 2,
      "total": 1
    }
  },
  "data": [
    {
      "id": 1,
      "name": "Dagdeel 1",
      "description": "",
      "status": "active",
      "date": "20160301",
      "start_time": "09:00",
      "end_time": "17:00",
      "is_elearning": 0,
      "visible": true,
      "instructors": {
        "id": 1,
        "href": "https://apitest.api.planaday.nl/v1/instructor/1"
      },
      "users": {
        "scheduled": 10,
        "options": 0,
        "waitinglist": 2
      },
      "locations": [
        {
          "id": 1,
          "href": "https://apitest.api.planaday.nl/v1/location/1"
        }
      ],
      "costs": {
        "user": "0",
        "vat": "0"
      },
      "has_code95": 1,
      "code95": {
        "practice": 12,
        "theory": 5
      },
      "labels": [
        "CODE95"
      ],
      "attributes": {
        "Take exam": {
          "code": "{182BF814-FAA4-1C3C-941A-FCD180A78033}",
          "values": {
            "ja": {
              "value": "50",
              "description": "display tekst"
            },
            "nee": {
              "value": "0",
              "description": "display tekst"
            }
          },
          "is_required": true,
          "multiple_values_allowed": false,
          "is_financial": true,
          "vat": 21,
          "vat_code": 2
        }
      },
      "href": "https://apitest.api.planaday.nl/v1/daypart/1"
    },
    {
      "id": 2,
      "name": "Dagdeel 2: elearning",
      "description": "",
      "status": "active",
      "date": "20200901",
      "start_time": "09:00",
      "end_time": "17:00",
      "is_elearning": 1,
      "visible": true,
      "date_finish_before": "20210901",
      "instructors": {
        "id": 1,
        "href": "https://apitest.api.planaday.nl/v1/instructor/1"
      },
      "users": {
        "scheduled": 10,
        "options": 0,
        "waitinglist": 2
      },
      "locations": [
        {
          "id": 2,
          "href": "https://apitest.api.planaday.nl/v1/location/2"
        }
      ],
      "costs": {
        "user": "0",
        "vat": "0",
        "vat_code": 0
      },
      "has_code95": 0,
      "labels": [
        "EHBO",
        "BHV"
      ],
      "href": "https://apitest.api.planaday.nl/v1/daypart/2"
    }
  ],
  "links": {
    "self": "https://apitest.api.planaday.nl/v1/course/1/dayparts?limit=20&offset=20",
    "first": "https://apitest.api.planaday.nl/v1/course/1/dayparts?limit=20&offset=1",
    "last": "https://apitest.api.planaday.nl/v1/course/1/dayparts?limit=20&offset=40",
    "next": "https://apitest.api.planaday.nl/v1/course/1/dayparts?limit=20&offset=40",
    "previous": "https://apitest.api.planaday.nl/v1/course/1/dayparts?limit=20&offset=1"
  }
}

View list of dayparts of Course
GET/course/{course_id}/dayparts?offset={offset}&limit={limit}

Retrieve all dayparts (sessions) for a specific course with full details per daypart. Use this to display a complete course schedule with dates, times, locations, instructors, and per-daypart costs.

Error codes

Code Message
401 No Access Right
404 Course not found
URI Parameters
HideShow
course_id
number (required) 

ID of the Course in the form of an integer

offset
number (optional) 

Start offset of dayparts to show (i.e. 25)

limit
number (optional) 

Amount of dayparts to show in one response (i.e. 25, limited to 100)


Course materials

GET https://apitest.api.planaday.nl/v1/course/course_id/materials?offset=offset&limit=limit&label=
Responses200
Headers
Content-Type: application/json
Body
{
  "meta": {
    "page": {
      "offset": 1,
      "limit": 20,
      "total": 2
    }
  },
  "data": [
    {
      "id": 450,
      "code": "b997b",
      "name": "Brandblusser stift",
      "description": "",
      "selling_price": 125
    },
    {
      "id": 450,
      "code": "0460d",
      "name": "Verband schrift",
      "description": "",
      "selling_price": 88,
      "vat": 21,
      "vat_code": 2
    }
  ],
  "links": {
    "self": "https://apitest.api.planaday.nl/v1/course/1/materials?limit=20&offset=20",
    "first": "https://apitest.api.planaday.nl/v1/course/1/materials?limit=20&offset=1",
    "last": "https://apitest.api.planaday.nl/v1/course/1/materials?limit=20&offset=40",
    "next": "https://apitest.api.planaday.nl/v1/course/1/materials?limit=20&offset=40",
    "previous": "https://apitest.api.planaday.nl/v1/course/1/materials?limit=20&offset=1"
  }
}

View list of materials of Course
GET/course/{course_id}/materials?offset={offset}&limit={limit}&label=

Retrieve the materials (e.g. course books, equipment, supplies) included in a course. Use this to display required materials to students before enrollment, or to generate a materials overview for planning purposes.

Error codes

Code Message
401 No Access Right
404 Course not found
URI Parameters
HideShow
course_id
number (required) 

ID of the Course in the form of an integer

offset
number (optional) 

Start offset of materials to show (i.e. 25)

limit
number (optional) 

Amount of materials to show in one response (i.e. 25, limited to 100)


Course images

GET https://apitest.api.planaday.nl/v1/course/course_id/images?offset=offset&limit=limit
Responses200
Headers
Content-Type: application/json
Body
{
  "meta": {
    "page": {
      "current": 1,
      "offset": 1,
      "limit": 20,
      "total": 1
    },
    "records": 1
  },
  "data": [
    {
      "id": 1,
      "name": "rookworst",
      "description": "",
      "href": "https://apitest.api.planaday.nl/v1/image/26S96G?crc=1b26f92dd44d7bea4e109f569447c4a2"
    }
  ],
  "links": {
    "self": "https://apitest.api.planaday.nl/v1/course/1/materials?limit=20&offset=20",
    "first": "https://apitest.api.planaday.nl/v1/course/1/materials?limit=20&offset=1",
    "last": "https://apitest.api.planaday.nl/v1/course/1/materials?limit=20&offset=40",
    "next": "https://apitest.api.planaday.nl/v1/course/1/materials?limit=20&offset=40",
    "previous": "https://apitest.api.planaday.nl/v1/course/1/materials?limit=20&offset=1"
  }
}

View list of images of Course
GET/course/{course_id}/images?offset={offset}&limit={limit}

Use this call to fetch the images of one (1) course.

Error codes

Code Message
401 No Access Right
404 Course not found
URI Parameters
HideShow
course_id
number (required) 

ID of the Course in the form of an integer

offset
number (optional) 

Start offset of images to show (i.e. 25)

limit
number (optional) 

Amount of images to show in one response (i.e. 25, limited to 100)


Coursetemplate

A course template is the blueprint for a course. It defines the structure (number of dayparts, costs, labels, attributes) that individual course instances inherit. For example, a “BHV Herhaling” template defines a 1-day course with specific pricing — each planned instance of that course in the calendar is based on this template.

List Coursetemplates

GET https://apitest.api.planaday.nl/v1/coursetemplate/list
Requestsexample 1
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "coursetemplates": [
    {
      "id": 1,
      "code": "a5f-#Y#M-#3C",
      "name": "Achterdeur maken",
      "description": null,
      "daypart_amount": 1,
      "has_elearning": true,
      "has_code95": false,
      "has_soob": false,
      "has_stap": false,
      "stap_only": false,
      "labels": [
        "CODE95"
      ],
      "language": "NL",
      "href": "https://apitest.api.planaday.nl/v1/coursetemplate/1"
    },
    {
      "id": 2,
      "code": "b44-#Y#M-#3C",
      "name": "Band repareren",
      "description": null,
      "daypart_amount": 3,
      "has_elearning": false,
      "has_code95": false,
      "has_soob": false,
      "has_stap": false,
      "stap_only": false,
      "labels": [
        "EHBO",
        "BHV"
      ],
      "language": "NL",
      "href": "https://apitest.api.planaday.nl/v1/coursetemplate/2"
    }
  ]
}

Get list of Coursetemplates
GET/coursetemplate/list

Retrieve all course templates that have at least one daypart defined. Use this to build a course catalog that shows what types of courses are offered, independent of specific dates or scheduling.

Error codes

Code Message
400 Bad Request
401 No Access Right

Get Coursetemplate

GET https://apitest.api.planaday.nl/v1/coursetemplate/coursetemplate_id
Requestsexample 1
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "id": 1,
  "href": "https://apitest.api.planaday.nl/v1/coursetemplate/1",
  "code": "511-#Y#M-#3C",
  "name": "Televisie opkopen",
  "description": null,
  "daypart_amount": 2,
  "dayparts": [
    {
      "id": 2,
      "href": "https://apitest.api.planaday.nl/v1/daypart/2"
    },
    {
      "id": 1,
      "href": "https://apitest.api.planaday.nl/v1/daypart/1"
    }
  ],
  "users": {
    "min": 5,
    "max": 12
  },
  "costs": {
    "user": 384,
    "course": 3179,
    "vat": 21,
    "vat_code": 2,
    "remark": null
  },
  "level": "Advanced",
  "has_elearning": 1,
  "has_code95": false,
  "has_soob": false,
  "has_stap": false,
  "stap_only": false,
  "labels": [
    "EHBO",
    "CODE95"
  ],
  "attributes": {
    "Take exam": {
      "code": "{182BF814-FAA4-1C3C-941A-FCD180A78033}",
      "values": {
        "ja": {
          "value": "50",
          "description": "display tekst"
        },
        "nee": {
          "value": "0",
          "description": "display tekst"
        }
      },
      "is_required": true,
      "multiple_values_allowed": false,
      "is_financial": true,
      "vat": 21,
      "vat_code": 2
    }
  },
  "language": "NL"
}

View Coursetemplate Detail
GET/coursetemplate/{coursetemplate_id}

Retrieve detailed information about a single course template, including its dayparts, pricing, attributes, and labels. Use this to display template details or to understand the structure before fetching individual course instances based on this template.

Error codes

Code Message
400 Bad Request
401 No Access Right
404 Coursetemplate not found
URI Parameters
HideShow
coursetemplate_id
number (required) 

ID of the coursetemplate in the form of an integer


Daypart

A daypart (dagdeel) is a single session within a course — for example, “Morning session: Theory” or “Day 2: Practical exam”. Each daypart has its own date, time, location, instructor, and optionally its own costs and attributes. Daypart IDs are returned by the course list and course detail endpoints.

Get Daypart

GET https://apitest.api.planaday.nl/v1/daypart/daypart_id?batch=batchlist
Requestsexample 1Answer with extrafieldswith batch parameter
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "href": "https://apitest.api.planaday.nl/v1/daypart/1",
  "id": 1,
  "code": null,
  "name": "Dagdeel #0",
  "description": null,
  "status": "concept",
  "date": "20200710",
  "date_finish_before": "20210710",
  "start_time": "12:30",
  "end_time": "17:00",
  "locations": [
    {
      "id": 1,
      "type": "virtual",
      "href": "https://apitest.api.planaday.nl/v1/location/1?type=virtual"
    }
  ],
  "instructors": [
    {
      "id": 1,
      "href": "https://apitest.api.planaday.nl/v1/instructor/1"
    }
  ],
  "users": {
    "scheduled": 0,
    "options": 0,
    "waitinglist": 0
  },
  "costs": {
    "user": null,
    "vat": 21,
    "vat_code": 2
  },
  "has_code95": 1,
  "code95": {
    "practice": 12,
    "theory": 5
  },
  "is_elearning": 1,
  "visible": true,
  "labels": [
    "CODE95"
  ],
  "attributes": {
    "Take exam": {
      "code": "{182BF814-FAA4-1C3C-941A-FCD180A78033}",
      "values": {
        "ja": {
          "value": "50",
          "description": "display tekst"
        },
        "nee": {
          "value": "0",
          "description": "display tekst"
        }
      },
      "is_required": true,
      "multiple_values_allowed": false,
      "is_financial": true,
      "vat": 21,
      "vat_code": 2
    }
  }
}
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "href": "https://apitest.api.planaday.nl/v1/daypart/1",
  "id": 1,
  "code": null,
  "name": "Dagdeel #0",
  "description": null,
  "status": "concept",
  "date": "20200710",
  "date_finish_before": "20210710",
  "start_time": "12:30",
  "end_time": "17:00",
  "locations": [
    {
      "id": 1,
      "type": "incompany",
      "href": "https://apitest.api.planaday.nl/v1/location/1?type=incompany"
    }
  ],
  "instructors": [
    {
      "id": 1,
      "href": "https://apitest.api.planaday.nl/v1/instructor/1"
    }
  ],
  "users": {
    "scheduled": 0,
    "options": 0,
    "waitinglist": 0
  },
  "costs": {
    "user": null,
    "vat": 21,
    "vat_code": 2
  },
  "has_code95": 1,
  "code95": {
    "practice": 12,
    "theory": 5
  },
  "is_elearning": 1,
  "visible": true,
  "extrafields": {
    "Startdatum": "23-08-2021"
  },
  "labels": [
    "CODE95"
  ]
}
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
[
  {
    "href": "https://apitest.api.planaday.nl/v1/daypart/1",
    "id": 1,
    "code": null,
    "name": "Dagdeel #0",
    ...
  },
  {
    "href": "https://apitest.api.planaday.nl/v1/daypart/2",
    "id": 2,
    "code": null,
    "name": "Dagdeel #2",
    ...
  }
]

View Daypart Detail
GET/daypart/{daypart_id}?batch={batchlist}

Retrieve detailed information about a daypart, including date, time, location, instructor, costs, labels, and attributes. Use this to display a full course schedule with per-session details.

Use ?batch= to fetch multiple dayparts in one call — for example, when loading all dayparts from the daypart_ids array returned by the course list endpoint.

Error codes

Code Message
400 Bad Request
401 No Access Right
404 Daypart not found
URI Parameters
HideShow
daypart_id
number (required) 

ID of the Daypart in the form of an integer

batchlist
string (required) 

List of IDs separated by + (plus)


Daypart materials

GET https://apitest.api.planaday.nl/v1/daypart/daypart_id/materials?offset=offset&limit=limit&label=
Responses200
Headers
Content-Type: application/json
Body
{
  "meta": {
    "page": {
      "offset": 0,
      "limit": 20,
      "total": 1
    }
  },
  "data": [
    {
      "id": 450,
      "code": "b997b",
      "name": "Brandblusser stift",
      "description": "",
      "selling_price": 125,
      "vat": 21,
      "vat_code": 2
    }
  ],
  "links": {
    "self": "https://apitest.api.planaday.nl/v1/daypart/1/materials?limit=20&offset=20",
    "first": "https://apitest.api.planaday.nl/v1/daypart/1/materials?limit=20&offset=1",
    "last": "https://apitest.api.planaday.nl/v1/daypart/1/materials?limit=20&offset=40",
    "next": "https://apitest.api.planaday.nl/v1/daypart/1/materials?limit=20&offset=40",
    "previous": "https://apitest.api.planaday.nl/v1/daypart/1/materials?limit=20&offset=1"
  }
}

View list of materials of a Daypart
GET/daypart/{daypart_id}/materials?offset={offset}&limit={limit}&label=

Retrieve the materials (e.g. course books, equipment) associated with a specific daypart. Use this to display required materials to students before a session, or to generate a materials checklist for instructors.

Error codes

Code Message
401 No Access Right
404 Daypart not found
URI Parameters
HideShow
daypart_id
number (required) 

ID of the Daypart in the form of an integer

offset
number (optional) 

Start offset of materials to show (i.e. 25)

limit
number (optional) 

Amount of materials to show in one response (i.e. 25, limited to 100)


Education

An education (opleiding) is a training program that groups multiple courses together into a structured learning path. For example, an “EHBO Instructor” education might consist of a theory course, a practical course, and an exam. Use these endpoints to display available education programs and their details on your website.

List educations

GET https://apitest.api.planaday.nl/v1/education/list
Requestsexample 1
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "educations": [
    {
      "id": 1,
      "code": "a5f-#Y#M-#3C",
      "name": "Achterdeur maken",
      "description": null,
      "course_amount": 1,
      "has_elearning": true,
      "has_code95": false,
      "has_soob": false,
      "labels": [
        "CODE95"
      ],
      "href": "https://apitest.api.planaday.nl/v1/education/1"
    },
    {
      "id": 2,
      "code": "b44-#Y#M-#3C",
      "name": "Band repareren",
      "description": null,
      "course_amount": 3,
      "has_elearning": false,
      "has_code95": false,
      "has_soob": false,
      "labels": [
        "EHBO",
        "BHV"
      ],
      "href": "https://apitest.api.planaday.nl/v1/education/2"
    }
  ]
}

Get list of Education
GET/education/list

Retrieve all available educations that have at least one active course. Use this to populate an education catalog or filter page on your website.

Error codes

Code Message
400 Bad Request
401 No Access Right

Get Education

GET https://apitest.api.planaday.nl/v1/education/education_id
Requestsexample 1
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "id": 1,
  "href": "https://apitest.api.planaday.nl/v1/education/1",
  "code": "511-#Y#M-#3C",
  "name": "Televisie opkopen",
  "description": null,
  "course_amount": 2,
  "courses": [
    {
      "id": 2,
      "href": "https://apitest.api.planaday.nl/v1/course/2"
    },
    {
      "id": 1,
      "href": "https://apitest.api.planaday.nl/v1/course/1"
    }
  ],
  "users": {
    "min": 5,
    "max": 12
  },
  "costs": {
    "user": 384,
    "vat": 21,
    "vat_code": 2,
    "remark": null
  },
  "has_elearning": 1,
  "has_code95": false,
  "has_soob": false,
  "labels": [
    "EHBO",
    "CODE95"
  ]
}

View Education Detail
GET/education/{education_id}

Retrieve detailed information about a single education, including its linked courses, price, and labels. Use this to display a full education detail page, showing all courses the student needs to complete and the total cost.

Error codes

Code Message
400 Bad Request
401 No Access Right
404 Education not found
URI Parameters
HideShow
education_id
number (required) 

ID of the education in the form of an integer


Extrafields

Extra fields are custom fields defined in the Planaday application. They extend the standard data model with organization-specific information (e.g. a custom “Certificate number” field for students, or a “Category” dropdown).

Extra fields are grouped by entity type (student, company, etc.) and each field has a type that determines how it should be rendered and validated.

Possible types

  • textfield — single-line text input

  • textarea — multi-line text input

  • datefield — date picker

  • checkbox — boolean on/off toggle

  • radiobutton — single selection from options

  • select — dropdown selection from options

Extrafields list

GET https://apitest.api.planaday.nl/v1/extrafields/list
Requestsexample 1
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "extrafields": {
    "student": [
      {
        "id": 50,
        "name": "Oranjekruis nummer",
        "description": "Het nummer van het Oranjekruis",
        "type": "textfield",
        "is_required": false,
        "default_value": null
      },
      {
        "id": 51,
        "name": "Categorie",
        "description": "Categorie van cursist",
        "type": "select",
        "is_required": true,
        "options": [
          "bhv",
          "brand",
          "ehbo"
        ],
        "default_value": "bhv"
      }
    ]
  }
}

Get a list of extrafields
GET/extrafields/list

Retrieve all extra field definitions grouped by entity type (e.g. “student”, “company”). Use this to discover which custom fields are available before creating bookings or students with extra field values. The field IDs returned here are the keys you pass in the extrafields block of the booking or student POST calls.

Error codes

Code Message
400 Bad Request
401 No Access Right

Image

Retrieve image files (photos, logos) associated with courses. Image IDs and URLs are returned by the course detail and course images endpoints.

Get Image

GET https://apitest.api.planaday.nl/v1/image/image_id
Requestsexample 1
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: image/*
Body
image blob

Get image blob
GET/image/{image_id}

Download the binary image file. Use this to display course images on your website or in your application. The image ID is obtained from the /course/{id}/images endpoint.

Error codes

Code Message
400 Bad Request
401 No Access Right
404 Image not found
URI Parameters
HideShow
image_id
string (required) 

ID of the Image (e.g. “26S96G”), as returned by the course images endpoint


Instructor

Manage instructors (trainers/teachers) and their assignments to courses and dayparts. Use these endpoints to look up instructor details, list all active instructors, and programmatically schedule or unschedule instructors for specific sessions or entire courses.

Get Instructor

GET https://apitest.api.planaday.nl/v1/instructor/instructor_id
Requestsexample 1
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "id": 23,
  "last_name": "Kaaskop",
  "first_name": "Kees",
  "initials": null,
  "prefix": null,
  "email": "kees@dezottekaaskop.nl",
  "phonenumber": "+31(0)6-12962699",
  "fields": {
    "Algemeen": "",
    "Website": "",
    "EHBO diplomanummer": "",
    "VCA diplomanummer": "",
    "Categorie": ""
  }
}

View Instructor Detail
GET/instructor/{instructor_id}

Retrieve detailed information about a single instructor, including contact details and custom fields. Use this to display instructor profiles on course detail pages or in booking confirmations.

Error codes

Code Message
400 Bad Request
401 No Access Rights
404 Instructor not found
URI Parameters
HideShow
instructor_id
number (required) 

ID of the instructor in the form of an integer


List instructors

GET https://apitest.api.planaday.nl/v1/instructor/list?offset=offset&limit=limit&label=label
Requestsexample 1
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "meta": {
    "page": {
      "current": 1,
      "offset": 1,
      "limit": 100,
      "total": 1
    },
    "records": 3
  },
  "data": [
    {
      "id": 1,
      "last_name": "Avci",
      "first_name": "Melle",
      "initials": null,
      "prefix": null,
      "href": "https://apitest.api.planaday.nl/v1/instructor/287"
    },
    {
      "id": 2,
      "last_name": "Sterkman",
      "first_name": "Bastiaan",
      "initials": null,
      "prefix": null,
      "href": "https://apitest.api.planaday.nl/v1/instructor/286"
    },
    {
      "id": 3,
      "last_name": "Zuidweg",
      "first_name": "Mila",
      "initials": null,
      "prefix": null,
      "href": "https://apitest.api.planaday.nl/v1/instructor/288"
    }
  ],
  "links": {
    "self": "https://apitest.api.planaday.nl/v1/instructor/list?offset=1",
    "first": "https://apitest.api.planaday.nl/v1/instructor/list?offset=1",
    "last": "https://apitest.api.planaday.nl/v1/instructor/list?offset=1",
    "previous": "",
    "next": ""
  }
}

View list of instructors
GET/instructor/list?offset={offset}&limit={limit}&label={label}

Retrieve a paginated list of all active instructors. Use this to populate instructor selection dropdowns or to build an instructor overview page.

Error codes

Code Message
401 No Access Right
URI Parameters
HideShow
offset
number (optional) 

Start offset of instructors to show (i.e. 25)

limit
number (optional) 

Amount of instructors to show in one response (i.e. 25, limited to 100)

label
string (optional) 

Filter instructors by label


Add/Remove instructor to/from a daypart

POST https://apitest.api.planaday.nl/v1/instructor/instructor_id/daypart
Requestsadd instructor to daypart with id 1
Headers
Content-Type: application/json
X-Api-Key: <apikey>
Body
{
  "daypart_id": 1,
  "create_task": true,
  "notify": true,
  "include_ical": true
}
Responses200
Headers
Content-Type: application/json
Body
{
  "message": "OK"
}

Add/plan an instructor to a specific daypart
POST/instructor/{instructor_id}/daypart

Schedule an instructor for a specific daypart. Optionally creates a task in Planaday and sends an email notification (with iCal attachment) to the instructor. Use this when assigning instructors to sessions from an external scheduling system.

Error codes

Code Message
401 No Access Right
401 Parameters missing: id
401 Missing or invalid json input
401 No email address known for this instructor
404 Instructor not found
404 Daypart not found
409 Instructor already planned for daypart
URI Parameters
HideShow
instructor_id
number (required) 

ID of the instructor in the form of an integer


DELETE https://apitest.api.planaday.nl/v1/instructor/instructor_id/daypart
Requestsdelete instructor from daypart with id 1
Headers
Content-Type: application/json
X-Api-Key: <apikey>
Body
{
  "daypart_id": 1,
  "notify": true
}
Responses204
This response has no content.

Remove/unplan an instructor from a specific daypart
DELETE/instructor/{instructor_id}/daypart

Remove an instructor from a specific daypart. Optionally sends a notification to the instructor about the change.

Error codes

Code Message
401 No Access Right
401 Parameters missing: id
401 Missing or invalid json input
401 No email address known for this instructor
404 Instructor not found
404 Daypart not found
404 Instructor not planned for daypart
URI Parameters
HideShow
instructor_id
number (required) 

ID of the instructor in the form of an integer


Add/remove instructor to/from a course

POST https://apitest.api.planaday.nl/v1/instructor/instructor_id/course
Requestsadd instructor to course with id 1
Headers
Content-Type: application/json
X-Api-Key: <apikey>
Body
{
  "course_id": 1,
  "create_task": true,
  "notify": true,
  "include_ical": true
}
Responses200
Headers
Content-Type: application/json
Body
{
  "message": "OK"
}

Add/plan an instructor to all dayparts in a specific course
POST/instructor/{instructor_id}/course

Schedule an instructor for all dayparts in a course at once. This is a convenience method — instead of calling the daypart endpoint for each session individually, this assigns the instructor to every daypart in the course.

Error codes

Code Message
401 No Access Right
401 Parameters missing: id
401 Missing or invalid json input
401 No email address known for this instructor
404 Instructor not found
404 Course not found
URI Parameters
HideShow
instructor_id
number (required) 

ID of the instructor in the form of an integer


DELETE https://apitest.api.planaday.nl/v1/instructor/instructor_id/course
Requestsdelete instructor from course with id 1
Headers
Content-Type: application/json
X-Api-Key: <apikey>
Body
{
  "course_id": 1,
  "notify": true
}
Responses204
This response has no content.

Remove/unplan an instructor from all dayparts in a specific course
DELETE/instructor/{instructor_id}/course

Remove an instructor from all dayparts in a course at once. Use this when an instructor becomes unavailable for an entire course.

Error codes

Code Message
401 No Access Right
401 Parameters missing: id
401 Missing or invalid json input
401 No email address known for this instructor
404 Instructor not found
404 Course not found
404 Instructor not planned for course
URI Parameters
HideShow
instructor_id
number (required) 

ID of the instructor in the form of an integer


Label

Labels are tags that can be attached to courses, dayparts, and other entities in Planaday. They are commonly used to categorize and filter content (e.g. “BHV”, “EHBO”, “CODE95”).

Label list

GET https://apitest.api.planaday.nl/v1/label/list
Requestsexample 1
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "labels": [
    {
      "id": 1,
      "name": "EHBO",
      "description": null
    },
    {
      "id": 2,
      "name": "CODE95",
      "description": null
    },
    {
      "id": 3,
      "name": "BHV",
      "description": "Alle BHV gerelateerde items"
    }
  ]
}

Get a list of labels
GET/label/list

Retrieve all labels defined in the system. Use this to populate filter dropdowns on your website, or to discover which labels are available before filtering courses or dayparts by label.

Error codes

Code Message
400 Bad Request
401 No Access Right

Location

Locations are venues where courses and dayparts take place. A location can be an internal training room, an external venue, a virtual classroom, or even a company address (for incompany courses). Location IDs are returned by the course and daypart detail endpoints.

Get Location

GET https://apitest.api.planaday.nl/v1/location/location_id?batch=batchlist&type=type
Requestsexample 1with batch argument
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "id": 1,
  "name": "Dolores.",
  "code": "65b77",
  "address": {
    "street_1": null,
    "street_2": null,
    "housenumber": 219,
    "housenumber_extension": null,
    "zipcode": "8618WY",
    "city": "Soerendonk",
    "country": "Nederland",
    "lat": 0,
    "lng": 0
  },
  "contact_info": {
    "phonenumber_1": null,
    "email": null,
    "website": "https://dolores.nu/locaties"
  },
  "description": null,
  "capacity": 11,
  "overbookable": false,
  "is_virtual": false,
  "is_external": true,
  "costs": {
    "price": 40,
    "vat": 21,
    "vat_code": 2
  },
  "href": "https://apitest.api.planaday.nl/v1/location/1"
}
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
[
  {
    "id": 1,
    "name": "Dolores.",
    "code": "65b77",
    ...
  },
  {
    "id": 2,
    "name": "Dolores B",
    "code": "65b88",
    ...
  }
]

View Location Detail
GET/location/{location_id}?batch={batchlist}&type={type}

Retrieve detailed information about a location, including address, capacity, costs, and contact info. Use this to display the venue details on a course detail page or in a booking confirmation.

Use ?batch= to fetch multiple locations in one call — for example, when a course has dayparts at different venues. Pass location IDs separated by a plus sign (+).

Use the type parameter when a location ID refers to different location types (e.g. a company used as an incompany venue).

Error codes

Code Message
400 Bad Request
401 No Access Right
404 Location not found
URI Parameters
HideShow
location_id
number (required) 

ID of the Location in the form of an integer

batchlist
string (required) 

List of IDs separated by + (plus)

type
string (required) 

Type of location to fetch (company, incompany, internal, external or virtual)


Ping

Health check endpoint to verify the API is up and running.

Ping

GET https://apitest.api.planaday.nl/v1/ping
Responses200
Headers
Content-Type: text/plain
Body
pong

Ping
GET/ping

Use this call to verify the API service is available. Returns pong on success. Useful for monitoring, load balancer health checks, or verifying API connectivity before making other calls.


Student

Manage student (cursist/medewerker) records in Planaday. Students are always linked to a company. Use this endpoint to synchronize employee/student data from an external HR or LMS system. The external_id field (typically the employee ID in your system) is used as the unique key for create-or-update logic.

Student

POST https://apitest.api.planaday.nl/v1/student
RequestsCreate new studentUpdate student
Headers
X-Api-Key: <apikey>
Body
{
  "active": true,
  "firstname": "Stefanos",
  "initials": "S.B.",
  "prefix": null,
  "lastname": "Verstegos",
  "address": {
    "street": "Logger",
    "housenumber": "123",
    "housenumber_extension": null,
    "postalcode": "1111AA",
    "city": "Amstelveen",
    "country": null
  },
  "contact": {
    "mobilenumber": "0612345679",
    "emailaddress_business": null,
    "emailaddress_private": "stefanos@prive.nl"
  },
  "date_of_birth": "2020-10-10",
  "employment": {
    "startdate": "2025-03-27",
    "enddate": null
  },
  "external_id": 112355,
  "company": {
    "id": null,
    "name": "Regio 11"
  },
  "remark": "Aanmaak"
}
Responses200200400404409
Headers
Content-Type: application/json
Body
{
  "external_id": 112355,
  "student_id": 1123,
  "company_id": 40,
  "message": "Student 1123 created"
}
Headers
Content-Type: application/json
Body
{
  "external_id": 112355,
  "student_id": 1123,
  "company_id": 40,
  "message": "Student 1123 updated"
}
Headers
Content-Type: application/json
Body
{
  "message": "Missing or invalid value for: [fieldname]"
}
Headers
Content-Type: application/json
Body
{
  "message": "Company not found"
}
Headers
Content-Type: application/json
Body
{
  "message": "External id conflict"
}
Headers
X-Api-Key: <apikey>
Body
{
  "active": true,
  "firstname": "Stefanos",
  "initials": "S.B.",
  "prefix": null,
  "lastname": "Verstegos",
  "address": {
    "street": "Logger",
    "housenumber": "123",
    "housenumber_extension": null,
    "postalcode": "1111AA",
    "city": "Amstelveen",
    "country": null
  },
  "contact": {
    "mobilenumber": "0612345679",
    "emailaddress_business": "stefanos@business.nl",
    "emailaddress_private": "stefanos@prive.nl"
  },
  "date_of_birth": "2020-10-10",
  "employment": {
    "startdate": "2025-03-27",
    "enddate": "2025-04-27"
  },
  "external_id": 112355,
  "company": {
    "id": 6,
    "name": null
  },
  "remark": "Omhangen naar ander bedrijf + uitdienst"
}
Responses200200400404409
Headers
Content-Type: application/json
Body
{
  "external_id": 112355,
  "student_id": 1123,
  "company_id": 40,
  "message": "Student 1123 created"
}
Headers
Content-Type: application/json
Body
{
  "external_id": 112355,
  "student_id": 1123,
  "company_id": 40,
  "message": "Student 1123 updated"
}
Headers
Content-Type: application/json
Body
{
  "message": "Missing or invalid value for: [fieldname]"
}
Headers
Content-Type: application/json
Body
{
  "message": "Company not found"
}
Headers
Content-Type: application/json
Body
{
  "message": "External id conflict"
}

Create or update a student
POST/student

Create a new student or update an existing one. The external_id determines whether to create or update — if a student with this external_id already exists, their data is updated. Every action creates an audit note on the student’s record.

Typical use cases:

  • Syncing employee data from an HR system (new hire → create, department change → update company)

  • Deactivating a student when they leave the organization (employment.active: false)

  • Moving a student to a different company when they transfer internally

Required fields

The following fields are required for all requests:

  • lastname - The student’s last name

  • external_id - A unique identifier for the student in your system (this is called employeeId in the booking call)

  • company.id OR company.name - The company the student belongs to (either ID or name must be provided)

The student will be created as a member of the specified company. If the company doesn’t exist and only the name is provided, a new company will be created.

Updating existing students

When posting a student with an existing external_id, the system will update the student’s information with the new data provided. Only the fields included in the request will be updated; omitted fields will remain unchanged.

When a company.id or company.name is provided that differs from the student’s current company, the student will be moved to the specified company.

Making students inactive

When you set the employment.active to false the student will be made inactive and all accounts will be deactivated.


Voucher

Vouchers provide discounts on courses. They can be percentage-based or fixed-amount, optionally limited to specific course templates, and have an availability count and expiration date. Use these endpoints to validate a voucher before applying it to a booking, and to mark it as used after a successful booking.

Voucher get

GET https://apitest.api.planaday.nl/v1/voucher/code
Requestsexample 1
Headers
X-Api-Key: <apikey>
Responses200
Headers
Content-Type: application/json
Body
{
  "id": 123,
  "code": "25KORTING",
  "type": "percentage",
  "amount": 25,
  "vat": 21,
  "is_for_coursetemplate": true,
  "coursetemplate_id": 16,
  "available": 25,
  "available_until": "2025-10-31"
}

Get a voucher
GET/voucher/{code}

Validate a voucher code and retrieve its details (discount type, amount, availability, expiration). Use this before completing a booking to verify the voucher is still valid and to display the discount to the customer.

Error codes

Code Message
400 Bad Request
401 No Access Right
402 Voucher not found
403 Voucher is not valid anymore
URI Parameters
HideShow
code
string (required) 

The voucher code as entered by the customer (e.g. “25KORTING”)


Voucher used

POST https://apitest.api.planaday.nl/v1/voucher/used/code
RequestsMark voucher as used
Headers
Content-Type: application/json
X-Api-Key: <apikey>
Body
{
  "booking_id": "API5ce597c40e0fc6.85265205"
}
Responses200
Headers
Content-Type: application/json
Body
{
  "status": "OK"
}

Mark a voucher as used for a booking
POST/voucher/used/{code}

Mark a voucher as consumed after a successful booking. This decreases the voucher’s availability count and creates a note on the student’s record documenting which course the voucher was applied to. Call this after the booking POST returns successfully.

Error codes

Code Message
400 Bad Request
401 Voucher not found
402 Unable to parse posted json
403 Empty or missing booking_id
404 Unknown booking selected
405 Voucher is not valid anymore
URI Parameters
HideShow
code
string (required) 

Lookup code of a voucher (string)


Changelist

Here you will find a list of changes made to the API.

Symbol Description
+ Added
- Removed
* Bugfix
. General
! Breaking change

17-03-2026

What Changes
. Rewrote all endpoint descriptions with use cases, examples, and context for when to use each call
. Added domain explanations to group descriptions (what is an education, daypart, course template, etc.)
. Fixed JSON syntax errors in body examples across all endpoints (invalid brackets, trailing commas, duplicate keys, PHP syntax)
. Fixed indentation inconsistencies in JSON body examples that broke aglio rendering
. Fixed URI template issues in instructor (missing leading /, wrong query param separator)
. Fixed typos and grammar across all endpoint documentation
. Improved clarity of batch request, extrafields, attributes and booking documentation

12-03-2026

What Changes
+ Added new call ‘GET /education/list’
+ Added new call ‘GET /education/{id}’
* Update call ‘GET /course/{id}’, added optional education fields

History 2025

Date What Changes
18-09-2025 * Update call ‘GET /voucher/{code}’
19-08-2025 + Added new call ‘GET /voucher/{code}’
15-08-2025 + Added new field in booking POST call ‘company_id’
25-07-2025 + Added new data in response of ‘GET /course/list’, daypart_amounts block
25-07-2025 + Added new data in response of ‘GET /course/{id}’, daypart_amounts block
16-07-2025 + Added new data in response of ‘GET /course/list’, type in costblock + daypart_display_type
16-07-2025 + Added new data in response of ‘GET /course/{id}’, type in costblock + daypart_display_type
21-06-2025 + Added new call ‘GET /material/{lookupcode}’
06-06-2025 + Added ?batch argument to call ‘GET /course/{courseId}’
06-06-2025 + Added ?batch argument to call ‘GET /daypart/{daypartId}’
06-06-2025 + Added ?batch argument to call ‘GET /location/{locationId}’
18-05-2025 . Improved descriptions and formatting in existing API documentation
18-05-2025 * Fixed JSON syntax errors in booking API documentation
14-05-2025 + Added new student (with amounts) block to call ‘GET /company/list’
14-05-2025 + Added new call ‘POST /company/transferstudents’
25-04-2025 + Added new call ‘POST /student’
25-04-2025 . Updated documentation of ‘GET /company/list’ and ‘GET /student/list’
20-04-2025 + Added new call ‘GET /company/list’
20-04-2025 + Added new call ‘POST /student’

History 2023

Date What Changes
25-09-2023 . Added naming overview to the documentation
11-01-2023 ! Freefields part in ‘GET /course/{courseId}’ changed to 'extrafields
11-01-2023 + Added new call ‘GET /extrafields/list’

History 2022

Date What Changes
15-11-2022 + Added new calls for instructor
15-11-2022 + Added instructor block in ‘GET /course/{courseId}/dayparts’
15-11-2022 + Added instructor block in ‘GET /daypart/{daypartId}’
10-10-2022 + Added optional ‘has_stap’ & ‘stap_only’ to course block in ‘GET /course/list’
10-10-2022 + Added optional ‘has_stap’ & ‘stap_only’ to course block in ‘GET /course/{courseId}’
10-10-2022 + Added optional ‘has_stap’ & ‘stap_only’ to course block in ‘GET /coursetemplate/list’
10-10-2022 + Added optional ‘has_stap’ & ‘stap_only’ to course block in ‘GET /coursetemplate/{courseTemplateId}’
10-10-2022 + Added optional ‘stap_regulation’ to student block in ‘POST /booking/{courseId}’
11-02-2022 + Added new call for (uptime) monitoring ‘GET ping’
11-02-2022 + Added optional ‘chamber_of_commerce’ to company block in ‘POST /booking/{courseId}’
11-02-2022 + Added optional ‘vat_number’ to company block in ‘POST /booking/{courseId}’
11-02-2022 + Added optional ‘invoice’ block to company block in ‘POST /booking/{courseId}’
11-02-2022 + Added optional ‘mailing’ block to company block in ‘POST /booking/{courseId}’
02-02-2022 + Added optional ‘prefix’ to students block for ‘POST booking/{courseId}’
02-02-2022 + Added optional ‘maiden_name’ to students block for ‘POST booking/{courseId}’

History 2021

Date What Changes
10-12-2021 + Added ’ Added new call to fetch images from course ‘GET /course/{courseId}/images’
10-12-2021 + Added ’ Added new call to serve 1 image ‘GET /image/{id}/{crc}’
21-09-2021 + Added ‘status’ in daypart block to ‘GET /course/{courseId}’
21-09-2021 + Added ‘status’ in daypart block to ‘GET /course/{courseId}/dayparts’
21-09-2021 + Added ‘status’ in daypart block to ‘GET /daypart/{daypartId}’
23-08-2021 + Added optional ‘freefields’ to ‘GET /course/{courseId}’
02-08-2021 + Added ‘visible’ to daypart block call ‘GET /course/list’
02-08-2021 + Added ‘visible’ to daypart block call ‘GET /course/{courseId}’
02-08-2021 + Added ‘visible’ to daypart block call ‘GET /course/dayparts/{courseId}’
02-08-2021 + Added ‘visible’ to daypart block call ‘GET /daypart/{daypartId}’
02-08-2021 + Added optional ‘label’ field to 'POST /course/booking/{courseId}
01-05-2021 + Introduced the Attributes blocks for coursetemplates, courses and dayparts
01-05-2021 + Introduced the Attributes blocks for the ‘POST /booking’ call
01-05-2021 . Added more example requests to several calls
01-05-2021 + Added optional ‘nick_name’ to booking call ‘POST /course/booking/{courseId}’
28-04-2021 + Added optional ‘language’ to booking call ‘POST /course/booking/{courseId}’
28-04-2021 + Added ‘language’ to call ‘GET /course/{courseId}’
28-04-2021 + Added ‘language’ to call ‘GET /course/list’
28-04-2021 + Added ‘language’ to call ‘GET /coursetemplate/{courseId}’
28-04-2021 + Added ‘language’ to call ‘GET /coursetemplate/list’
15-30-2021 + Added optional ‘process_code95’ and ‘process_soob’ in the ‘student’ block to call ‘POST /course/booking/{courseId}’
17-02-2021 + Added ‘vat’ and ‘vat_code’ to call ‘GET /course/{courseId}/materials’
17-02-2021 + Added ‘vat’ and ‘vat_code’ to call ‘GET /daypart/{daypartId}/materials’
17-02-2021 + Added ‘vat_code’ to ‘costs’ block for call ‘GET /course/{courseId}’
17-02-2021 + Added ‘vat_code’ to ‘costs’ block for call ‘GET /course/list’
17-02-2021 + Added ‘vat_code’ to ‘costs’ block for call ‘GET /coursetemplate/{coursetemplateId}’
17-02-2021 + Added ‘vat_code’ to ‘costs’ block for call ‘GET /daypart/{daypartId}’
17-02-2021 + Added ‘vat_code’ to ‘costs’ block for call ‘GET /location/{locationId}’
17-02-2021 + Added ‘options’ to ‘users’ block for call ‘GET /course/list’
17-02-2021 + Added ‘options’ to ‘users’ block for call ‘GET /course/{courseId}’
13-01-2021 . Added information about Ratelimiting

History 2020

Date What Changes
23-11-2020 + Added ‘has_code95’ and ‘has_soob’ to ‘GET /course/list’
23-11-2020 + Added ‘has_code95’ and ‘has_soob’ to GET /coursetemplate/list
23-11-2020 + Added ‘has_code95’ and ‘has_soob’ to GET /coursetemplate/{id}
18-11-2020 + Added ‘labels’ to ‘GET /course/{id}/dayparts’
18-11-2020 + Added ‘labels’ to ‘GET /daypart/{id}’
18-11-2020 + Added ‘labels’ in daypart blocks to ‘GET /course/list’
18-11-2020 + Added ‘labels’ in daypart blocks to ‘GET /coursetemplate/list’
30-10-2020 + Added optional ‘internal_reference’ to student block for call POST /booking/{courseId}
30-10-2020 + Added DELETE call to delete previous create bookings for callDELETE /booking/{id}
30-10-2020 + Added ‘status’ course call (GET /course/{id})
30-10-2020 + Added ‘status’ course list call (GET /course/list)
20-10-2020 + Added ‘has_code95’, ‘code95’ and ‘has_soob’ (GET /course/{id})
20-10-2020 + Added ‘has_code95’ and ‘code95’ (GET /course/dayparts/{id})
20-10-2020 + Added ‘has_code95’ and ‘code95’ (GET /daypart/{id})
27-08-2020 + Added new call to fetch labels (GET label/list)
27-08-2020 + Added label to arguments to fetch courses with have a certain label (GET /course/list)
27-08-2020 + Added ‘labels’ to ‘GET /coursetemplate’
27-08-2020 + Added ‘labels’ to ‘GET /course’ and 'GET /course/list
21-08-2020 + Added ‘payment’ block to ‘POST /booking/{courseId}’
21-08-2020 + Added new call to mark booking as payed (POST /booking/payed/{id})
21-08-2020 + Added ‘is_payed’ to ‘POST booking’
17-08-2020 . It’s now possible to see difference between normal (offline) and e-Learning (online) dayparts
17-08-2020 + Added is_elearning to ‘GET /daypart’
17-08-2020 + Added date_finish_before to ‘GET /daypart’
17-08-2020 + Added has_elearning to ‘GET /coursetemplate’
17-08-2020 + Added has_elearning to ‘GET /coursetemplate/list’
17-08-2020 + Added has_elearning to ‘GET /course’
17-08-2020 + Added has_elearning to ‘GET /course/list’
17-08-2020 + Added is_elearning to ‘GET /course/dayparts’
17-08-2020 + Added date_finish_before to ‘GET /course/dayparts’
31-07-2020 + Added new fields ‘invoice_mail’ and ‘remark’ to ‘create booking call’
31-07-2020 * Updated gender values in ‘create booking call’. Added ‘u’ (unknown) and ‘nr’ (not relevant)
19-05-2020 + Added new call to fetch materials of a course
19-05-2020 + Added new call to fetch materials of a daypart
19-05-2020 + Added an optional block ‘materials’ to the ‘create booking call’
18-05-2020 + Added an optional block ‘dayparts’ to the ‘create booking call’
18-05-2020 * Made the ‘email’ field in ‘student’ section of the ‘create booking call’ optional. May be null
03-02-2020 + Added optional ‘is_student’ to ‘create booking call’ in ‘students’ section. Default value: true
03-02-2020 . New layout (three columns) for API docs
03-02-2020 + Added optional ‘is_contact_person’ to ‘create booking call’ in ‘students’ section. Default value: false
18-10-2019 + Added changelist in documentation
18-10-2019 * Fixed description about offsets in documentation
01-01-2019 . Initial setup of API

Generated by aglio on 17 Mar 2026