ainmai Create an API key

Public API · v2.1

Build on the people data you already keep.

The ainm.ai API lists and updates employees, attendance, and leave for one company. Named keys, a shared rate limit, and signed webhooks. Payroll reads are in the reference and marked coming soon.

Getting started

Base URL https://ainm.ai/api/v2. Send JSON. Every route except GET / requires a bearer key.

Create a key

  1. Sign in as a company admin.
  2. Open ainm.ai/api-keys (Public API & Integrations). It is not in the sidebar.
  3. Choose Generate Key, name it for one integration, and leave only the permissions that integration needs. New keys start with every permission on.
  4. Copy the key. It begins with ak_ and is shown once. ainm.ai stores only a SHA-256 hash.

Revoking one key does not affect the others. Demo companies cannot create keys.

Authenticate

Authorization: Bearer ak_your_key_here

A missing, malformed, unknown, or revoked key is 401. A valid key without the permission for that route is 403. An id from another company is 404, the same as a missing id.

Permissions

PermissionRoutes
read_employees / write_employees/employees
read_attendance / write_attendance/attendance, clock-in, clock-out
read_leave / write_leave/leave, approve, reject
read_payroll/payroll/runs, /payroll/payslips · Coming soon

Rate limit

100 requests per 60 seconds per key, shared by every API worker. The 100th request is allowed. The next is 429 with Retry-After in seconds, and it still counts. 401 does not count.

After a key is accepted the response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (UTC unix time).

Pagination and errors

Lists return { data, meta }. meta is total, page, per_page, pages. page starts at 1, per_page defaults to 25 and caps at 100. pages is at least 1.

401 403 404 409 and 429 use {"detail": "..."}. 422 is a validation list, except an unknown leave status, which is {"detail": "Invalid status: ..."}.

Payroll is coming soon. The runs and payslips routes are in the spec. ainm.ai payroll is not live, so do not build a production payroll integration yet. Creating an employee here does not send an invite email, and the temporary password is not returned.

Reference

Rendered from /api/v2/openapi.json. Swagger UI remains at /api/v2/docs and ReDoc at /api/v2/redoc.

Loading the OpenAPI document…

Examples

Replace ak_your_key_here. These calls read or create employees. The create call does not email the person.

List employees

curl -sS "https://ainm.ai/api/v2/employees?page=1&per_page=25" \
  -H "Authorization: Bearer ak_your_key_here" \
  -H "Accept: application/json"

Create an employee

curl -sS -X POST "https://ainm.ai/api/v2/employees" \
  -H "Authorization: Bearer ak_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"email":"aoife.murphy@example.com","first_name":"Aoife","last_name":"Murphy","job_title":"People Partner","department":"People","start_date":"2026-02-01"}'

Webhooks Available

Outgoing webhooks are wired. ainm.ai POSTs a signed JSON body when the event happens in the product or through this API. Register a public https:// URL on the Webhooks tab at ainm.ai/api-keys. Inactive companies are not delivered to. Demo companies cannot register a URL.

Events

EventWhen it fires
employee.createdInvite, CSV import, or POST /employees
employee.updatedProfile update in the product, or PATCH /employees/{id} when a field was sent
leave.requestedLeave submitted in the product or via POST /leave
leave.approvedLeave approved in the product or via the approve route
expense.submittedAn expense is submitted in the product. This API has no expenses route.
attendance.clockinClock-in in the product or via POST /attendance/clock-in

Rejecting leave, clocking out, and payroll reads do not emit an event. webhook.test is not subscribable. Use Send test event on a webhook to receive one signed sample.

Signature

Headers: X-Ainm-Timestamp (unix seconds), X-Ainm-Signature (v1= plus hex HMAC-SHA256), X-Ainm-Event, X-Ainm-Delivery. The MAC covers timestamp + "." + raw_body and the per-webhook secret. Reject timestamps more than five minutes off. The JSON id stays the same across retries, so dedupe on it. Each retry has a new timestamp and signature.

Failures are retried after about 30 seconds, 2 minutes, 10 minutes, and 30 minutes, then marked failed. Redirects are not followed.

import hmac, hashlib, time

def verify(secret: str, timestamp: str, body: bytes, signature: str) -> bool:
    if abs(int(time.time()) - int(timestamp)) > 300:
        return False
    expected = hmac.new(
        secret.encode(),
        timestamp.encode() + b"." + body,
        hashlib.sha256,
    ).hexdigest()
    presented = signature.removeprefix("v1=")
    return hmac.compare_digest(expected, presented)

Changelog

22 October 2026 · Documentation

This guide, and a fuller OpenAPI document: permissions, examples, pagination, rate-limit headers, and error bodies. Request and response JSON is unchanged. Payroll routes are marked coming soon.

21 October 2026 · v2.1

Signed webhook delivery, a rate limit of 100 requests per minute shared across API workers, and more than one named key per company. Resource paths and JSON shapes from v2.0 are unchanged.

22 March 2026 · v2.0

First public REST API: API-key access to employees, attendance, leave, and read-only payroll routes.