Skip to main content

API: users and codes

General contract in How the API works. This is the most integrated part of the API: syncing the client's headcount with INSSACS consumers.

Consumer users

POST /consumers
GET /consumers
GET /consumers/summary
GET /consumers/:id
PATCH /consumers/:id
PATCH /consumers/:id/active
DELETE /consumers/:id
GET /consumers/:id/entitlements

Creating — 201

FieldTypeRequiredNotes
namestringyes
identificationstringyesunique within the account
tags{ key, value }[]nokey must exist in the account's tags
accountIdstringnoignored: the key's account wins
initialCodeobjectnocreates the first code in the same call
curl -X POST https://api.inssacs.com/consumers \
-H 'x-api-key: ics_…' -H 'content-type: application/json' \
-d '{
"name": "Juan Pérez",
"identification": "1023456789",
"tags": [{ "key": "departamento", "value": "Producción" }],
"initialCode": { "codeId": "1023456789", "codeType": "QR", "operationId": "665f…c7" }
}'

Response: the consumer with their codes.

{
"id": "665f…e1",
"clientId": "665f…c5",
"accountId": "665f…c6",
"name": "Juan Pérez",
"identification": "1023456789",
"isActive": true,
"tags": [{ "key": "departamento", "value": "Producción" }],
"codes": [
{
"codeId": "1023456789",
"codeType": "QR",
"operationId": "665f…c7",
"isActive": true,
"vendingProfileId": null,
"easProfileId": null,
"vendingBalance": 0,
"vendingRecharge": 0
}
],
"createdAt": "2026-08-11T14:03:00.000Z"
}

Editing and status

  • PATCH /consumers/:idname, identification, tags (optional). 200.
  • PATCH /consumers/:id/active with { "active": false } → deactivates the user and with them all their codes. 200.
  • DELETE /consumers/:id204; removes codes, tags and assigned EAS products. Historical deliveries are kept.

Querying

  • GET /consumers — query: operationId, isActive, search (name, ID number or code), page, pageSize (max 1000).
  • GET /consumers/summary — counts of users and codes, active and inactive.
  • GET /consumers/:id/entitlements — what that user may take today: handy to show quotas in your own portal.

Errors: 409 consumers.identification_exists, 404 consumers.not_found, 400 consumers.unknown_tag_key, 400 consumers.too_many_tags.

Codes

POST /consumers/:id/codes
PATCH /consumers/:id/codes/:codeId
DELETE /consumers/:id/codes/:codeId
PATCH /consumers/:id/codes/:codeId/active
PUT /consumers/:id/codes/:codeId/profile
DELETE /consumers/:id/codes/:codeId/profile/:businessLine

Adding a code — 201

FieldTypeRequiredNotes
codeIdstringyesthe value the machine reads
codeTypestringyesQR, NFC, FINGERPRINT, CODE128, CODE39, PDF417, EAN13, UPCA, MAXICODE, MRZ
operationIdstringnoomit = account-level code: same value, balance and quota across every operation
vending.balanceintegernoinitial balance in minor units

A code is born with no profiles: its lines are defined by assigning them.

Assigning and removing a profile

PUT /consumers/:id/codes/:codeId/profile { "profileId": "665f…d1" }
DELETE /consumers/:id/codes/:codeId/profile/vending
DELETE /consumers/:id/codes/:codeId/profile/eas

The line comes from the profile: a vending profile fills the vending slot. The profile must belong to the user's account (400 consumers.profile_not_for_this_account) and the code must keep at least one profile (409 consumers.code_requires_profile).

Editing a code

PATCH /consumers/:id/codes/:codeId:

FieldTypeNotes
newCodeIdstringchanges the value; uniqueness is re-validated
codeTypestringchanging away from FINGERPRINT deletes the fingerprints
vendingBalanceintegersets the subsidy balance
operationIdstring | nullmoves level; null = becomes account-level. Keeps balance and profiles
lineProfilesobjectsets both profiles at once

Value conflicts when moving level: 409 consumers.code_value_taken_in_operation and 409 consumers.code_value_taken_at_account.

Status and removal

  • PATCH …/active with { "active": false } → disables just that code.
  • DELETE …204. History is kept.

Fingerprints

GET /consumers/:consumerId/codes/:codeId/fingerprints
POST /consumers/:consumerId/codes/:codeId/fingerprints
DELETE /consumers/:consumerId/codes/:codeId/fingerprints/:fingerId

FINGERPRINT codes only. The POST expects the finger and its samples, already captured by the reader:

FieldTypeRequiredNotes
fingerIdinteger 0–9yeswhich finger
fingerLabelstringnoRight index
fingerprintsobject[]yesthe samples (base64 template)

Errors: 422 consumers.no_fingerprint_template, 409 consumers.fingerprint_already_enrolled, 409 consumers.too_many_fingerprints, 400 consumers.code_not_fingerprint.

The template comes from the reader

The API does not capture fingerprints: it receives the templates produced by the reader's local agent. Without that agent there is no enrolment.

Bulk loads

POST /consumers/bulk { "items": [ … ] }
POST /consumers/codes/bulk { "items": [ … ] }

Each item carries its op field plus that operation's data:

Endpointop values
/consumers/bulkcreate, update, delete, upsert
/consumers/codes/bulkcreate, update, delete
{
"items": [
{ "op": "create", "name": "Juan Pérez", "identification": "1023456789" },
{ "op": "update", "identification": "1010087922", "name": "Ana Ruiz" }
]
}

The response is 201 with the per-row outcome: one invalid row does not abort the batch.

{
"summary": { "created": 1, "updated": 1, "deleted": 0, "failed": 0 },
"results": [{ "index": 0, "op": "create", "status": "created", "id": "665f…e9" }]
}

This is the recommended path for syncing headcount: one call instead of N.

The batch permission

Because the batch is multi-op, it does not require one specific action but at least one of create, update or delete on the resource (consumers for the first, codes for the second). With none, it answers 403. Then each row is authorised by its own operation: with only create, the update or delete rows fail individually and the rest of the batch goes through.