Developers

Guide

Power Automate connector

Power Automate reaches an external API through a custom connector. Kelomo ships one ready to import, covering the work a Microsoft 365 organisation asks for: keep the customer, project, task and tracking-target registers in step with the system that owns them, look a record up by its own key, record and decide work days and absences, and start a flow when Kelomo changes something.

Import the connector

  1. Download apiDefinition.swagger.json.
  2. In make.powerautomate.com, open Data → Custom connectors → New custom connector → Import an OpenAPI file and pick the file.
  3. Leave the security tab as imported: API key in the Authorization header.
  4. Create a connection and paste the whole header value, including the word Bearer: Bearer kelomo_….
  5. Test it with Check the connection (GET /v1/me/access), which every key can reach whatever its scopes.

The connector is a deliberate slice of the API, not all of it: a custom connector lists every operation in one flat picker. A flow that needs more builds its own connector from the full Swagger 2.0 download, or calls the API with the generic HTTP action.

Which key

The four create or update actions call import routes, which require an org.admin service key that is not read-only. The tenant must also hold the module behind the register — project_tracking, crm or field_service for customers, project_tracking for projects and tasks. Tracking targets are base product. The find actions need the capability behind each list; the tracking-level catalogue and its values are reachable by any key. The trigger needs integrations.manage plus the capability the event carries — the event catalogue names both.

The registers are kept, not re-created

Every import action sends rows, and the row is matched on its external reference first and its code second. The reference is a namespace and a key: name the system, the instance and the record type (dynamics_contoso_account, planner_contoso_task) so two sources can never collide inside one Kelomo organisation.

Create or update customers — request
{
  "upsert": true,
  "rows": [
    {
      "name": "Acme Oy",
      "code": "acme",
      "externalSource": "dynamics_contoso_account",
      "externalId": "701"
    }
  ]
}

upsert: true is what turns the route from a one-off import into a sync: left out, a row whose code already exists is skipped, which is the original import's published behaviour. The task action needs no such flag — a row carrying a reference is the sync row there.

Response
{
  "results": [
    { "index": 0, "status": "unchanged", "id": "b8c9…", "code": null, "targetCode": "acme" }
  ]
}

The answer is always 200 with one result per row: a partial import is a report, not an HTTP failure. status is created, updated, unchanged, skipped or error; code is the machine-readable reason a row was refused, and targetCode the record's own code. A flow that runs every ten minutes spends most of its life on unchanged, which writes nothing and leaves no audit line.

Add a Condition on body('Create_or_update_customers')?['results'][0]?['status'] when the flow should branch on the outcome, and treat error as a stop: the common reasons are external_ref_conflict (the code belongs to another source), code_in_use_unlinked (turn on linkByCode for the first run over hand-made records) and missing_match_key (the row named neither a code nor a reference).

Looking a record up

Every register list takes the same exact filters, so a flow holding a source key can ask Kelomo for that one record rather than paging a register.

Find projects by external reference
GET /v1/projects?externalSource=planner_contoso_plan&externalId=AAMkAG…
Authorization: Bearer kelomo_…

Customers and projects also take code; tasks are looked up inside their project by the reference or by number, the running number in the task key <project code>-<number>. An exact lookup answers with the record even when it has been deactivated — the caller holds the key and reads active itself.

Work days and approvals

Replace my work day's entries and Replace a member's work day entries send the day's whole row list: the route replaces the day. To add an entry, read the day with Get my work day or Get a member's work day, send its entries back with the new one — minus any row whose sourceAbsencePeriodDayId is set, a decided absence's credit that Kelomo attaches itself — and pass the day's updatedAt as expectedUpdatedAt. An edit in between answers 409 day_changed; a confirmed day answers 409 day_confirmed_locked. The written day stays a draft the employee confirms.

Approve a work day and Return a work day take the workDayId of a workday.confirmed event; Approve an absence and Reject an absence take the periodId of absence.submitted. Invite a member creates and invites in one call and requires an Idempotency-Key header — the flow's run id is a good one. These actions need worktime.supervise, hr.manage (absences) or members.manage on the key.

The event trigger

When a Kelomo event fires subscribes the flow to one event and delivers { id, event, createdAt, data }. Payloads carry identifiers, state and timestamps only — never person names — so the pattern is trigger, then a find action, then the work.

Deleting the flow removes the subscription. Power Automate unsubscribes by calling the URL the subscribe response returns in its Location header, and POST /v1/webhooks answers 201 with { id, secret } and that header. To remove a subscription while the flow lives on, use the connector's Delete an event subscription action or the list under Settings → Integrations → Webhooks.

Two worked flows

Cost centres from the accounting system become tracking targets. A recurring flow reads the cost-centre list, and for each row runs Create or update tracking targets with the Kelomo tracking level's code as level, the cost-centre code as code and netvisor_12345_costcentre as the source. The code never changes in a later run, which is what keeps the payroll and accounting exports matching.

A Planner plan becomes a project, its tasks tasks. Trigger on When a new task is created (Planner), run Create or update projects with the plan id as the reference, then Create or update tasks with the project's code and the Planner task id. Map the Planner status to externalStatus and add those words in Kelomo under Settings → Projects → Status mappings first: an unmapped word stops the flow (status_mapping_missing) rather than guessing a board column.

Both flows are written from the connector definition and the API contract; neither has yet been run against a real Microsoft 365 tenant.