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
- Download apiDefinition.swagger.json.
- In make.powerautomate.com, open Data → Custom connectors → New custom connector → Import an OpenAPI file and pick the file.
-
Leave the security tab as imported: API key in the
Authorizationheader. -
Create a connection and paste the whole header value, including the word
Bearer:Bearer kelomo_…. -
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.
{
"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.
{
"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.
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.