Skip to main content
Version: API v3.0.0 (Latest)

Migrating from 2.x

Version 3.0.0 is a clean break, and the version lives in the URL — so nothing you have today changes until you point a call at the new path. You can move one endpoint at a time.

1. Change the URL​

- https://apigw.goninjio.com/api/api_gateway/customer/employees/
+ https://apigw.goninjio.com/api/api_gateway/3.0.0/customer/employees/

Your API key and the apiKey header are unchanged, and so are the scopes each endpoint requires.

2. Read rows from data​

This is the change that touches the most code, and it is mechanical. In 2.x, rows came back under eight different keys — or under none at all.

Endpoint2.x rows were at3.0.0
/customer/employees/employeesdata
/customer/employee/deletion_logs/logsdata
/customer/users/usersdata
/customer/groups/(bare array)data
/customer/groups/tags/(bare array)data
/customer/groups/dynamic/phishing/datadata
/customer/groups/dynamic/training/datadata
/customer/campaigns/phishing/(bare array)data
/customer/campaigns/training/(bare array)data
/customer/simulations/phishing/simulationsdata
/customer/simulations/training/trainingsdata
/customer/templates/phishing/templatesdata
/customer/templates/email/templatesdata
/customer/templates/training/templatesdata
/customer/templates/training/performance/{id}/data.employees_datadata
/customer/simulations/training/{id}/performance/data.employees_datadata
/customer/bundles/(bare array)data
/customer/domains/(bare array)data
/tenant/bundles/(bare array)data
/tenant/direct_customers/(bare array)data
/tenant/hierarchy/(bare array)data
/tenant/collections/datadata
/tenant/mass_campaign_delivery/datadata
The bare-array endpoints need a parser change

Nine endpoints used to return a JSON array at the top level and now return an object. If you did for row in response.json(), that becomes for row in response.json()["data"].

3. Add a pagination loop where you relied on getting everything​

Those same nine endpoints were also unbounded — they returned every row, every time. From 3.0.0 they return a page, so anywhere you assumed one response contained the whole set, follow next_cursor. See Pagination.

4. Rename the page-size parameter​

2.x spelled this four different ways depending on the endpoint. 3.0.0 accepts one:

2.x3.0.0
page_sizepage_size
per_page (deletion logs)page_size
items_per_pagepage_size
amount (mass delivery)page_size

5. Move off the duplicate envelope keys​

2.x emitted the same two numbers under four extra names. They are gone:

RemovedUse instead
per_pagepage_size
totaltotal_items
total_pagesnum_pages
current_pagepage

6. Expect 404 where you got 400​

In 2.x a resource that did not exist could answer 400, 404, a Django HTML error page, or even 200 with an empty object, depending on which endpoint you asked. In 3.0.0 it is always 404 with a JSON body.

Notably, these used to answer 400:

  • a campaign, simulation or dynamic group id that does not exist (or belongs to another account)
  • an employee that does not exist
  • /customer/learner-report/ when the account has no report

And /customer/templates/phishing/{id}/ used to return an HTML 404 page from inside a JSON API. It now returns {"error": "..."} like everything else.

7. Error bodies are one shape​

2.x used eight between them — error, errors, message, details, validation_error, a raw list of validation objects, and plain text. 3.0.0 uses exactly one:

{ "error": "'page_size' must not exceed 1000" }

Other differences worth knowing​

Timestamps are 2026-01-15T10:30:00+00:00 everywhere. 2.x used three different formats, one of which (/tenant/collections/) dropped the seconds and the timezone entirely, so those values gain precision.

Some listings carry more fields than their 2.x counterpart. The template catalogs now always include level, quiz_average and quiz_events_count; dynamic group details include type and is_training. These are additions — nothing was removed.

/customer/simulations/phishing/ and /customer/simulations/training/ defaulted to 20 rows per page in 2.x. They default to 100 now, like every other listing.

A page past the last one now returns an empty page, reporting the page number you asked for. 2.x was inconsistent here: some endpoints raised a 500 (the training template performance endpoint among them), others quietly answered with the last page, so a client that overran received rows it had not asked for and had no way to tell.

Filtering by a date covers the whole of the day named. In 2.x a bare created_date[lte]=2026-03-01 was read as midnight, so nearly all of 1 March fell outside the range. Expect row counts to differ when you compare the two versions on a date filter; 3.0.0 is the one that matches what the parameter says. The filters_applied echo reports the exact instant compared against.

Two endpoints that error on 2.x answer normally on 3.0.0. Fetching a single training campaign raises a 500 on 2.x whenever its simulations carry an emotion, and the phishing campaign listing raises a 500 if any campaign has no start or end date. Both are served correctly here. 2.x is left as it is, because changing those responses would break callers that have already worked around them.

Deleted records are no longer listed — on either version. A simulation, campaign or mass delivery the customer deleted used to appear in listings even though the web UI had stopped showing it, which put test data into reports and skewed statistics. Both 2.x and 3.0.0 now leave them out, and both accept ?include_deleted=true when you want the full history. Fetching one by id is unchanged and still returns the record whatever its state — but the lists inside a detail response are not: a campaign's simulations_ids and emotions, and a template's template_simulations, all leave deleted simulations out now, on both versions and with no flag. See Filtering. If your integration filtered these out client-side, that code can go.

/customer/templates/phishing/search/ is available on both versions. It existed on 2.x but was never registered with the gateway's authorisation layer, so every call to it was refused with 403. If you ruled it out for that reason, it works now.

Not yet available in 3.0.0​

These still exist on their 2.x paths and are unchanged there. Keep calling them at /api/api_gateway/... until they land:

EndpointMethods
/customer/employees/POST
/customer/employees/{employee_id}/PUT, DELETE
/customer/employees/{employee_id}/training/performance/GET
/customer/groups/POST
/customer/groups/{group_id}/PUT
/customer/campaigns/phishing/POST
/customer/campaigns/training/POST
/customer/campaigns/training/{campaign_id}/status/PUT
/customer/simulations/training/POST
/customer/templates/training/statistics/{template_id}/GET
/customer/users/POST
/customer/users/{user_id}/DELETE
/customer/logo/POST
/customer/learner-report/PUT, DELETE
/customer/billing_report/download_csv/GET
/tenant/billing_report/download_csv/GET
/tenant/create/POST
/tenant/customer/create/POST

Mixing versions is fine: read through 3.0.0 and write through 2.x until the write endpoints are published.

Checklist​

  1. Point one endpoint at /3.0.0/ in a test environment.
  2. Change your row accessor to data.
  3. Add a pagination loop wherever you relied on receiving everything.
  4. Rename per_page / items_per_page / amount to page_size.
  5. Replace total, total_pages, current_page, per_page with the canonical names.
  6. Treat 404 as "not found" where you previously special-cased 400.
  7. Use filters_applied in your tests to confirm the server read your filters the way you intended.