Skip to main content

The audit API

The endpoints behind Settings → Compliance and audit: process controls and their exceptions, populations, samples and evidence packs, the configuration, conflicts of duties and the export log; the controls and approvals on a case; and the exports of every list. What they are for is in compliance and audit.

[!IMPORTANT] Each endpoint also needs its permission from whoever calls it. The endpoints under /api/v1/audit need a token with the admin scope (API tokens), which only Administrators can give; people in the app need only the permission.

Process controls and exceptions​

All under /api/v1/audit, in an organization (orgId).

EndpointPermissionWhat
GET controls?orgId=audit.evidence or control.settingsThe organization's controls, each with counts: met, not met, explained.
POST controlscontrol.settingsAdd one: { orgId, name, description, reference, subject, scope, requirement, config, mode, enabled }.
GET controls/<id>audit.evidence or control.settingsA control with its history: every version, its kind (created, changed, enabled, disabled), who, when, and the control as it was.
PATCH controls/<id>control.settingsChange it, or switch it off or on ({ enabled }): a new version in its history.
GET exceptions?orgId=&from=&to=&controlId=&format=audit.evidence or control.settingsRecords a control wasn't met on, in the period (the last year by default), with their explanations. format: json (the default), csv, xlsx or pdf.
POST exceptions/<id>/explaincontrol.settings{ explanation }: why the control wasn't met on that record.

A control's fields​

FieldValues
subjectcase or incident (with Incident Management on).
scope{ unitIds, formIds, labels, priorities, categoryIds, props }, each a list; empty lists match everything. A unit matches records in it and below.
requirementcustomer_approval and agent_approval (cases), four_eyes, field (with config: { field }, a data property's key), sla_met, and review (incidents).
modeenforced or checked (the default), below.
ModeWhat happens
enforcedResolving and closing are refused until it is met; the answer is 400 with the control's name and what is missing.
checkedThe default: the result is recorded, a miss is an exception.

Evidence​

EndpointPermissionWhat
GET evidence/sourcesaudit.evidenceThe kinds of record evidence comes from (cases, and with their plugins on incidents, problems, changes) and their filters.
GET evidence/population?source=&orgId=&from=&to=&format=audit.evidenceEvery record of the source created in the period that you can see, with the source's filters as more query fields (cases: unitId, formId, label, priority, status). source=control&controlId= gives every record a control was recorded on. JSON has the total and the first 500 rows; format=csv, xlsx or pdf gives the whole population as a file.
POST evidence/samplesaudit.evidenceDraw a sample: { name, source, orgId, from, to, controlId, filters, method }, with method: random and size (up to 500) and an optional seed, or method: pasted and refs (up to 2,000; 400 not_in_population names those that aren't in it). Kept with the population's count and SHA-256, and the seed.
GET evidence/samples?orgId=, GET evidence/samples/<id>audit.evidenceThe samples drawn, newest first; one sample.
GET evidence/samples/<id>/pack.zipaudit.evidenceThe evidence pack: a PDF per record, index.xlsx with each file's SHA-256 and an About this pack sheet, and README.txt.
GET evidence/records/<source>/<id>.pdfaudit.evidence or report.readOne record as evidence: cases, incidents, problems or changes, and the record's id.

[!NOTE] The same seed and population always give the same random sample.

Configuration, conflicts and the export log​

EndpointPermissionWhat
GET configuration?format=audit.configThe configuration as it is, as sheets (name, columns, rows): Roles, Role holders, People, Units, Service levels, Automations, Controls, Housekeeping, Forms, Plugins. format=xlsx or pdf for a file.
GET conflicts?format=audit.config{ conflicts, admins, rules }: active people holding pairs of permissions that shouldn't usually go together, with why, and the Administrators apart. format=csv, xlsx or pdf for a file.
GET exports?kind=audit.readThe export log: every export with who, when, what (kind, title, format), the filters, the rows and the checksums (sha256 of the file, dataSha256 of the rows). kind matches the start (cases, evidence).

Controls and approvals on a case​

Under /api/v1/cases/<ref>, with cases:read (and cases:write to ask or decide). The case must be one you can see.

EndpointWhat
GET controlsThe controls that apply: each with met and detail now, and recorded (what was recorded when it was resolved, with any explanation).
GET approvalsThe case's approvals: kind (customer or agent), the question, status (pending, approved, rejected, void), who asked, who decided (a person, or the requester as decidedByContact), the comment, the address, sha256 (the fingerprint of the subject and request fields) and stale (they changed since).
POST approvals{ kind, question }. customer sends the question to the requester as a reply (a token also needs cases:reply); agent asks someone with case.approve. 409 pending when one of the kind waits already; earlier answers of the kind become void.
POST approvals/<id>/decision{ decision: approve or reject, comment }, for an agent approval: needs case.approve where the case is; 403 separation_of_duties for the case's assignee and whoever asked; 409 changed when the request changed since it was asked. Rejecting needs a comment.
GET report.pdfThe case as a PDF report: its details, request, approvals, history and controls.

An approval's status​

pending → approved
pending → rejected: the approver says no, with a comment
approved → void: a new approval of the same kind is asked
rejected → void: a new approval of the same kind is asked

The requester's decision​

The requester decides on their case page, through /api/v1/public/cases/<ref>/approvals (signed with the case link's cookie, and the X-Desk-Customer: 1 header):

  1. GET lists what waits for them.
  2. POST <id>/code emails a 6-digit code (valid 10 minutes; at most 5 in 15 minutes, 429 too_many_codes).
  3. POST <id>/decision takes { decision, comment, code } (5 tries per code, 400 bad_code).

Exports​

Every list has an export with the list's own filters, as query fields, and:

  • format: csv (the default), xlsx or pdf;
  • columns: column ids in order, or all of them when left out.
EndpointToken scopeUp to
GET /api/v1/cases/exportcases:read20,000 cases
GET /api/v1/x/incidents/exportincidents:read10,000 incidents
GET /api/v1/x/problems/exportproblems:read20,000 problems
GET /api/v1/x/changes/exportchanges:read20,000 changes
GET /api/v1/audit/log/export?from=&to=&q=admin (and audit.read)100,000 entries (the last 90 days by default)

Every file carries its metadata:

FormatWhere
ExcelA sheet About this export: the export id, who, when, rows, filters, and the SHA-256 of the rows as CSV.
PDFThe same on the first page and in each page's footer.

Every response has the headers:

HeaderWhat
X-Export-IdThe export's id in the export log (exp_…).
X-Checksum-SHA256The SHA-256 of the file.
X-Rows-SHA256The SHA-256 of the rows as CSV: the same for every format of the same list.

Every export is in the export log (GET /api/v1/audit/exports) and the audit log (export.created).

Roles for a period​

POST /api/v1/role-assignments takes endsAt (an ISO time, in the future) with the role, the person or team and the unit. The role stops counting then, and the hourly job removes it; the access history records it as revoked.

Giving the same role again changes its end. Role holders carry endsAt.

[!NOTE] Like the rest of giving roles, this needs role.write and a person in the app, not a token.