Technical documentation

Microbizz REST API

Authentication:

  • To Stay Logged In: Use an access token. The preferred method is a Bearer Token, where the value to look for is access_token.


Base URLs:

  • New Part: /Microbizz/Api/php/microbizz.php/CONTRACT_NUMBER/

  • Old Part: /api/rest/v1/microbizz.php/CONTRACT_NUMBER/


API Endpoints:

  1. Get Access Token:

    • Endpoint: /getaccesstoken?username=ABCDEF@microbizz.com&password=XXXXXXXX

    • Method: GET

    • Description: Retrieves an access token for authentication. The access token should be used in subsequent calls to the REST API, by specifying it in a HTTP header field, like this: Authorization: Bearer MYACCESSTOKEN, or by specifying it in a GET parameter named access_token.

  2. Retrieve Object:

    • Endpoint: /OBJECT/OBJECTID

    • Method: GET

    • Description: Fetches details of an object by its ID.

  3. Find Objects with Query:

    • Endpoint: /OBJECT?query={"type": "GreaterEqual", "fieldname": "", "value": "2024-05-18 09:13:50"}

    • Method: GET (FIND)

    • Description: Searches for objects based on a query.

  4. Create Object:

    • Endpoint: /OBJECT

    • Method: POST (CREATE)

    • Description: Creates a new object with JSON data in the request body.

  5. Update Object:

    • Endpoint: /OBJECT/OBJECTID

    • Method: POST (EDIT)

    • Description: Updates an existing object with JSON data in the request body.

  6. Perform Action on Object:

    • Endpoint: /OBJECT/OBJECTID/ACTION

    • Method: POST (ACTION)

    • Example: /todo/4/close

    • Description: Executes a specific action on an object.

  7. Delete Object:

    • Endpoint: /OBJECT/OBJECTID

    • Method: DELETE

    • Description: Deletes an object by its ID.

  8. Describe an Object Type:

    • Endpoint: /OBJECT/schema

    • Method: GET

    • Description: Answers the fields the object type has, with the type, whether each is required, read-only, searchable and sortable, and how it is written. The optional fields parameter limits it to the named fields, and naming a field that doesn't exist answers 400. It only names the fields, so it needs no access token and no permission, and it is read-only: any other method answers 405.

  9. Read Your Permissions:

    • Endpoint: /OBJECT/permissions and /OBJECT/OBJECTID/permissions

    • Method: GET

    • Description: Answers what the caller may do with the object type, or with that one object. See below.

  10. Contract-Specific Operations:

    • Endpoint: /CONTRACTNO/OBJECT/OBJECTID

    • Description: Used for operations that require a contract number.


Key Points:

  • Contract Number: Always include the contract number in the URL for both new and old API endpoints.

  • Authentication: Use the access_token in the Authorization header with the Bearer scheme for all requests after authentication.

  • Method Naming: The EDIT method should ideally be named UPDATE for clarity, but it currently uses the POST method for updates.


This structure provides a clear and concise overview of how to interact with the Microbizz API, ensuring users understand the importance of the contract number and the correct use of access tokens for authentication.



Object permissions

Every object on the new part reports what the caller is allowed to do with it, so a client can hide what it cannot use instead of discovering it through a 403.

Endpoint

Answers

GET /OBJECT/permissions

What the caller may do with objects of this type.

GET /OBJECT/OBJECTID/permissions

The same, narrowed by what that one object allows. A team that is referred to elsewhere reports delete as false, for instance.

Any authenticated user may ask, as the answer only describes their own permissions. Only GET is supported; anything else answers 405, and an object type that doesn't exist answers 404. Asking about a single object requires being allowed to read the type, so that the answer cannot be used to find out which IDs exist.

The four flags create, read, update and delete are always present, also when they are false, so nothing has to be read into their absence. special holds the permissions that are specific to the object, and is an object also when it is empty. The names in it differ per object — a role reports assign_users, a company subscriptions and edit_subscriptions — and more may be added over time, so read the ones you know and ignore the rest.

JSON
{"object":"role","id":7,"create":true,"read":true,"update":true,"delete":false,"special":{"assign_users":true}}

What is reported is what the API enforces. Every object type is guarded before any handler runs, so a request the permissions deny is refused without the body being looked at, and nothing is read or written. The only paths outside that are /OBJECT/schema, which just names the fields and needs no token, and /OBJECT/permissions itself.

Status

When

401

The access token is missing or invalid. This is answered before any permission is looked at, so a 401 never means the permissions are wrong.

403

The caller may not perform the operation on this object type, or not on this object.

404

The name is not an object type, or the object doesn't exist.

405

The method is not one of GET, POST and DELETE, or it is a non-GET on /permissions or a write to /schema.

Which permission each operation needs is decided per object, and the permissions are the same ones that gate the corresponding pages in the web system: whatever a user cannot do there, they cannot do through the API either. The codes per object are listed on Microbizz\Api, and /permissions is the answer for the caller at hand.

A permission that depends on the values being written cannot be answered before the request is read, so it is reported as a 400 with an explanation rather than a 403. Writing a work plan registration for another user is the example below.

Object commands

Besides the endpoints above an object may answer commands of its own on /OBJECT/OBJECTID/COMMAND, like the binary commands on a file and the membership commands on a role or a team. A command the object doesn't support answers 400.

A command is behind the permission of the method it uses, so a GET command needs read and a POST command needs update on the object. A command may require more than that, and the ones below say so where they do.

The Swagger export under Data > REST API lists the commands of every object together with the method they take.

Files and documents

The file object is available on both parts of the API and represents a document. Its content is not a field on the object, so it is transferred either as base64 inside the object or through the binary commands below.

Reading and writing the content

Command

Purpose

GET /file/FILEID/getbinary

Answers the document itself, with Content-Type, Content-Disposition and Content-Length taken from the document. Answers 404 when the document has no content. binary is an alias.

POST /file/FILEID/setbinary

Replaces the content, and answers the resulting filename and mimetype. binary is an alias.

Neither command base64 encodes the content, so they are the cheap way to move anything but a small file.

setbinary takes the content in one of two ways:

  • As the raw file in the request body. The Content-Type header states the mimetype and ?filename= names the file; the existing filename is kept when it is omitted.

  • As a file object in a JSON body, as described below.

A JSON body is only read as a file object when it holds at least one of the properties action, filename, mimetype or binary, so a JSON document can still be posted as raw content.

The file object

A field holding a file or an image is written by passing this object, which is also what setbinary accepts as a JSON body:

Property

Meaning

action

Either add to store the file given here, or remove to delete it. Everything else is optional when removing.

filename

Name of the file. Required unless temp_file is used.

mimetype

Mimetype of the content. An explicit mimetype wins over the one derived from an uploaded file.

binary

The content, base64 encoded.

temp_file

Name of a file already uploaded to the temp folder, as an alternative to binary.

When reading, a file field holds the internal ID of the file. Set include_binary_data=1 to have the content included as base64 as well; on the file object itself this adds a binary property. Responses can become very large, so prefer getbinary for anything but small files.

Roles and teams

The user module exposes roles and teams on the new part as the role and the team object. Both are read, created, updated and deleted like any other object, and both answer commands for the users they hold.

The objects

Object

Key

Fields

role

role_id

title, the name of the role, which is required when creating one. create_date and change_date are read-only.

team

id

name, the name of the team. create_date and change_date are read-only.

GET /role/schema and GET /team/schema answer the authoritative field list, and need no access token. Creating answers 201 with the object that was made, while a create that leaves out a required field answers 400 and stores nothing.

Deleting a role takes it off the users holding it and deletes the permissions granted to it. A team cannot be deleted while something still refers to it, which GET /team/TEAMID/permissions reports as delete being false.

The users of a role or a team

Command

Purpose

GET /role/ROLEID/users

Answers role_id and user_ids, the users holding this role.

POST /role/ROLEID/assignuser

Grants the role to the users in the body. Answers role_id, the resulting user_ids, and assigned with the users that were changed.

POST /role/ROLEID/unassignuser

Revokes the role from the users in the body. Answers the same, with unassigned.

GET /team/TEAMID/users

Answers id and user_ids, the members of this team.

POST /team/TEAMID/adduser

Puts the users in the body in the team. Answers id, the resulting user_ids, and added with the users that were changed.

POST /team/TEAMID/removeuser

Takes them out of the team. Answers the same, with removed.

The body names the users either as a single user_id or as a user_ids list, and may hold nothing else:

JSON
{"user_ids":[5,12]}

The commands are idempotent. A user who already holds the role, or already is in the team, is not reported as changed, and the call still answers 200. Every user in the body is checked before anything is changed, so a request naming one bad user changes nothing at all.

Status

When

400

The body is not a JSON object holding user_id or user_ids, holds another property, or names something that is not a user ID. Also the answer to a command the object doesn't support.

403

The caller may not use the command, or may not access one of the users named.

404

One of the users named doesn't exist.

Permissions

Roles and teams are administered in the user module, so they are guarded by the same permissions as the configuration pages for them:

Operation

role

team

create

roles

edit_team

read

roles

team

update

roles

edit_team

delete

roles

delete_team

the membership commands

roles and edit

edit_team and edit

All of them are permissions in the user module. The membership commands change the users rather than the role or the team, so they need permission to edit users on top of the permission that gets the request through. GET /role/permissions reports that as assign_users in special, and GET /team/permissions as add_users.

The roles a user holds in a team

Team roles are not on the team; they are on the user, in its teams field, and are written by posting that field to the user, eg. POST /user/42:

JSON
{"teams":[{"team":3,"team_roles":[7,9]}]}

Each element is an object of team, the ID of the team, and team_roles, the IDs of the roles the user holds in that team. team_roles may be left out or empty. The field is written as a whole, so a post has to carry every team the user is to be in.

A value that doesn't match that shape is refused with 400 and nothing is stored, whether it is a flat list of team IDs, an element carrying an unknown property, a team that doesn't exist, or a team_roles that isn't a list.

The roles a user holds outside of any team are the separate roles field, a list of role IDs, which is also what assignuser writes. adduser on a user who already is in the team changes nothing, so it never clears the team roles they hold, while removeuser drops them along with the membership.

Work plan

The workplan module exposes the work plan on the new part as two objects: workplan, which is one registration per user per date, and workplantype, the registration types the contract has defined. Both are kept in step with the work plan in the web system, so a change made on either side shows up on the other.

workplan

Field

Meaning

workplan_id

Key.

user_ref

The user the registration is for. Required.

date

The date it covers, as YYYY-MM-DD. Required.

registration

What the day is. Required. 0 non-work, 1 work, 2 out of house, 3 sick, 4 child sick, 5 vacation, 6 public holiday, 7 away, or the ID of a work plan type.

is_locked

Whether the registration is locked against further changes.

There is one registration per user per date. When a registration is changed from the web system the row is replaced rather than updated, so its ID changes; a client that has stored IDs should look a registration up by user_ref and date rather than assume the ID it saw is still there. A registration is also hard deleted, so it disappears from the list rather than being marked as deleted.

A work plan is only filled in from the day it is written or burned, so a date with no registration simply has no object. Until the work plan of an existing solution has been transferred to the new part, GET /workplan answers an empty list rather than an error.

workplantype

The eight built-in types, 0 to 7, are not objects: they are configured per contract and exist only as the numbers above. workplantype holds the custom types, and their IDs start above the built-in ones, so a registration value identifies a type across both kinds. A type whose ID would collide with a built-in one is refused.

Field

Meaning

regtype_id

Key, which is also the value a registration holds.

code

Short code shown in the work plan.

title

The name of the type. Required.

realtype

Which built-in type it behaves as, using the same numbers as registration.

color

Colour used in the work plan, as a hex value.

send_to_registration

Whether a time registration is made when the type is used.

register_time_on

What the time is registered on when it is sent to registration.

always_use_duration

Whether duration is used rather than the length of the day.

duration

The duration to register, in whole minutes.

internal_registration_ref, account_ref, task_ref, workarea_ref

The internal registration type, tracking account, task and work area the time is registered on, each as an ID or 0.

Permissions

Both objects are guarded by the workplan module:

Operation

workplan

workplantype

create, update, delete

edit

types

read

module

module

Everybody with edit may manage their own work plan; anybody else's takes edit_others on top, which GET /workplan/permissions reports in special. Without it, reading, changing or deleting another user's registration answers 403, while creating a registration for another user answers 400 with an explanation, as the user it is for is only known once the body has been read. This is stricter than the work plan page in the web system, which lets anybody with edit change any user's plan.

Reading a work plan type takes no more than access to the module, as every registration names one and would otherwise be a number with no meaning. Creating and changing types is the work plan configuration, and takes types.

Writable fields on the old part

The old part accepts more field types than it used to:

  • A reference to another object can be set by passing its ID. The object must exist, otherwise the call is rejected. Where the type does not identify one single class — a plain internal ID, or a type shared by several classes — the ID is stored without being verified.

  • Country fields are free text, as both codes and names occur in the database. A two letter code is upper-cased.

  • A module code must be a modcode the system knows.

  • HTML fields are reduced to the tags the editor allows, and broken HTML is repaired.

  • Fields holding whole numbers, eg. a duration in minutes, are stored as integers rather than floats.

  • An empty string is accepted for an email field. Filename fields cannot be set.

  • Any other type is rejected with an explanation rather than silently ignored.

When creating an object, the required fields are checked against what the request actually sent.

Query commands

Query commands should contain three fields: type, fieldname and value

Greater

Return everything where fieldname is greater than value.

GreaterEqual

Return everything where fieldname is greater or equal than value.

Lesser

Return everything where fieldname is lesser than value.

LesserEqual

Return everything where fieldname is lesser or equal than value.

Like

Return everything where value is contained in fieldname.

Match

Return everything where fieldname equals value.

OneOf

Return everything where fieldname is one of value (value should be an array).

Logical commands

Logical commands contains a type and one or more conditions which are further query or logical commands.

AND

condition1, condition2

Return everything which satisfies both condition1 and condition2.

OR

condition1, condition2

Return everything which satisfies either condition1 or condition2.

NOT

condition

Return everything which doesn't satisfy condition.

Examples

Find all data where age is equal or greater than 18

{"type":"GreaterEqual","fieldname":"age","value":18}


Find all data where age is less than 40 and gender is male

{"type":"AND","condition1":{"type":"Lesser","fieldname":"age","value":40},"condition2":{"type":"Match","fieldname":"gender","value":"male"}}

Find all data where firstname contains the letter a

{"type":"Like","fieldname":"firstname","value":"a"}

Find all data changed since a specific time stamp

{"type":"Greater","fieldname":"change_date","value":"2021-01-15 14:45:00"}