Skip to content

API access

mTime exposes a REST API for developers who want to integrate with or automate the system. This page is a high-level orientation — the OpenAPI specification is the authoritative reference for every endpoint, request/response schema, and required permission. Endpoint-level detail is intentionally not repeated here.

This page is aimed at developers. The rest of the Knowledge Center is written for everyday users.

Base URL

EnvironmentBase URL
Productionhttps://mtime.app/api
Staginghttps://staging-mtime.app/api

Each environment holds its own data and its own keys — a key issued in one never works in the other. GET /ping answers without credentials — it is the plain connectivity check.

Authentication

The API authenticates with API keys belonging to a service user.

  1. In Admin › Users, create a service user and give it an API key. Copy the key immediately — it is shown only once.

  2. Send the key on every request as a bearer token:

    Authorization: Bearer mtime_ak_...

Give the service user the role that covers the data your integration touches — the same roles described under users and permissions. Grant the least-privileged one that does the job: every endpoint is gated by a permission, and a missing one is what a 403 means.

The Developer role is not one of them. It controls whether the API reference appears in the app’s menu for a person signing in, and grants no data access, so it does nothing for a service user.

A service user is not linked to an employee, so permissions scoped to Managed or Self grant it nothing on endpoints that address a single employee (/employees/{employee_id}/…) — those requests are refused. Give an integration that works on employees’ behalf the All scope for the resources it touches.

To check a key, call GET /api/version: it takes any valid credential and requires no permission, so it answers 200 for a working key and 401 for a bad one, with no role in the way.

OpenAPI specification

The full contract is published as OpenAPI 3.1:

  • GET /api/openapi.yaml
  • GET /api/openapi.json
  • /api/docs renders it in the browser — production, staging

In the app, the reference sits under Resources › API Reference, and that entry needs the Developer role. Owners have it already — the Owner role includes Developer — and no other role shows the entry. Assign Developer in Admin › Users to whoever builds the integration: it opens the reference and nothing else, so it is safe to hand out.

It describes exactly the endpoints an API key may reach. An endpoint that is absent is not available to integrations, and calling it returns 404 rather than an explanation — so treat the spec as the list of what exists.

Generate a typed client from it rather than hand-writing request types. Each operation also declares the permission it requires (x-required-permission), so you can see up front what role your service user needs.

API areas

Operations in the spec are grouped by tag; that grouping is authoritative. These are the areas the API covers today — each links into the reference:

AreaPath prefixWhat it covers
Organization/employees, /org-unitsEmployees and the org-unit hierarchy — read, create and update
Settings/settings/…Activities, dimensions and time accounts (read, create, update), and employment terms (read)
Timesheet/employees/{employee_id}/timesheet, /timesheet/statementsAn employee’s timesheet, and the statements a period closes into
Timebank/employees/{employee_id}/timebank/balancesCurrent balances per account
Projects/projectsProjects — read, create and update
Reports/reports/…Registered hours, account movements, presence, absence, sickness and working-time compliance
Admin/admin/imports, /admin/exports, /admin/jobsWorkspace data import and export jobs, and triggering a test-only batch job on a test workspace and polling its run
Misc/ping, /versionReachability check and the API version

With an API key, imports are only allowed in a workspace marked as a test workspace, whatever the mode or scope. Only the test-only batch jobs — such as Seed timesheet pattern — may be triggered. Importing and exporting are owner permissions and a service user can’t be an owner, so give it a custom role with the import and export permissions alongside its other role.

Everything else the app itself uses — the rest of workspace administration, users and API keys, approvals, kiosks, time off requests, events and webhooks — is not part of the API today. Those endpoints are absent from the spec, and a key calling one gets 404.

Permissions

Every endpoint is gated by a permission (for example settings:create:*), resolved from the service user’s role. If a call returns 403, check the operation’s required permission in the spec against the service user’s role.

The domain model

A few concepts underpin most of the API:

  • Employment terms (the “agreement”) carry the contract-level settings of an employment: the work time norm, the flex account, the hourly pay activity, the default reason, and the timesheet-approval mode. Holiday calendars, policies (time off, allowance, work time and settlement) and reasons are not attached to a term — each one declares an availability scope (availability_scope) that decides which employments it applies to, by work country, org unit and employment terms.
  • Activities are the categories time is registered against. Each has a type — work, timeoff, non-work, or allowance — and can be nested (a child must share its parent’s type). An activity can require a project and/or custom dimensions.
  • Projects are work/billing buckets that registrations can be booked against, and can require a manager’s approval.
  • Dimensions are custom tags (for example a vehicle or piece of equipment) with a defined set of allowed values. Activities can require them, which validates what users may pick.
  • Time accounts hold balances — time off measured in days, or flex measured in hours.

Two layers of time

Time is recorded in two independent layers:

  • Presence — check-in/out records. A presence interval carries a reason (and optionally a note and activity). The rule engine evaluates presence intervals.
  • Entries — grid registrations that allocate time to an activity, a project, and dimension values.

By default these layers are independent; a work-time constraint can require that they reconcile. As a rule of thumb: when and why live on presence; what and where live on entries.

The rule engine

Premiums, overtime, on-call supplements and flex are calculated by policies built from plugins:

  • Allowance policies turn worked time into a payable amount — either via a norm-aware week counter (additional hours, overtime, norm deficit, worked hours) or via ordered rules (filters that select intervals + a calculator that turns them into an amount), optionally triggered by specific reasons, and registered onto a target allowance activity.
  • Work time policies carry working-time constraints and auto-registrations (such as a lunch deduction). The flex account itself is set on the employment terms.
  • Settlement policies are the period-close pipeline that credits counters to a time account and resolves payout versus flex.

Filters, calculators, counters, accruals, expirations, usage rules, holiday generators, and the various work-time and settlement steps are all plugins. Discover the available plugins and each one’s configuration schema at:

  • GET /api/descriptor/plugins/
  • GET /api/descriptor/plugins/{registry}/{plugin_id}/schema

These descriptor endpoints answer without a key, but they are not described in the spec — policies themselves are configured in the app.

Configuration order

Only part of the configuration is writable through the API today — activities, dimensions, time accounts, employees, org units and projects; calendars, reasons, policies and employment terms are set up in the app. Because entities reference one another, create them foundation-first: dimensions and time accounts → activities → org units → employees → projects.

Effective-dated versioning

Configuration entities and employee records are versioned. A change takes effect from its effective date, and a newly created entity is not effective before its creation date. To change a past period, add a retroactive version rather than editing the current one.

How results are produced

Worked totals, allowances, flex and payout are not computed live on the daily timesheet — they materialize when a timesheet statement is generated and then submitted and approved (settlement). Statement generation runs as a scheduled batch job; the payout-versus-flex amount is chosen at submission. Build integrations that read settled figures from statements and account-movement reports accordingly.

Listing and scope

Collections are paginated and filterable (see each operation in the spec). Some user-centric lists default to the caller’s own data — for example timesheet statements default to the caller’s scope; request the workspace-wide scope explicitly when your integration needs it.

A list’s limit is between 1 and 1000; leave it out to get the default. A value outside that range is refused with 422. /timesheet/statements returns at most 500 statements: when more matched, the response carries the header MTime-Truncated: true, so narrow the period or the filter.

The reference lists (accounts, activities, dimensions, employment terms, org units and projects) carry a weak ETag. Send it back in If-None-Match and an unchanged list answers 304 Not Modified with no body, which saves re-downloading it on every sync.

Rate limits

Each API key may make 300 requests per minute, and all keys of one workspace together 600, so creating more keys does not raise the allowance. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. A request over the limit gets 429 Too Many Requests with a Retry-After header that gives the seconds to wait. Back off and retry after that time.