The provider API
Sync clients and seats with your CRM automatically: keys, calls, responses and error codes.
1. What the API is for
The API syncs your clients and their seats with your CRM automatically. Everything the API does can also be done by hand in the provider portal.
Publishing maps and managing keys is done by a signed-in person in the portal. Those calls do not accept a key.
2. Keys and authentication
Create an API key in the provider portal. It is shown right after you create it and cannot be shown again after that.
You can use several keys at once. To rotate a key, create a new one, switch your system over and revoke the old one. A revoked key stops working immediately.
Send the key in the x-provider-key header or as Authorization: Bearer mf_…. The base URL is https://app.flow.jumoca.de/v1/provider, and requests and responses are JSON. The number of requests per minute is limited and applies to all your keys together.
A GET on the base URL returns the overview: your keys, clients, published maps and the number of billed seats. It also lists your clients’ IDs.
curl https://app.flow.jumoca.de/v1/provider \
-H "x-provider-key: mf_..." 3. Checking domains
POST /customers/check checks a list of domains. You can also send an email address instead of a domain, in which case the part after the @ counts. The check does not create anything.
curl -X POST https://app.flow.jumoca.de/v1/provider/customers/check \
-H "x-provider-key: mf_..." \
-H "content-type: application/json" \
-d '{ "domains": ["example.com", "example-group.com", "gmail.com"] }' Every result has a status. found means a Microsoft 365 organisation is behind the domain. not_found means there is none, so nobody at that client can sign in yet. invalid is not a valid domain, and own_tenant is a domain of your own organisation, which cannot be added.
sameAs lists the other domains in the same request that belong to the same organisation. They go into one entry.
{
"results": [
{ "domain": "example.com", "status": "found", "sameAs": ["example-group.com"] },
{ "domain": "example-group.com", "status": "found", "sameAs": ["example.com"] },
{ "domain": "gmail.com", "status": "not_found", "sameAs": [] }
]
} 4. Adding and updating clients
POST /customers adds clients or updates existing ones. You can send a single client or a list. domains names at least one of the client’s domains, reference is your own identifier, usually the number from your CRM, and seats is the number of seats.
Sending seats buys seats. Without seats, an existing client keeps its seats and a new one gets none. Sending the same list again changes nothing.
curl -X POST https://app.flow.jumoca.de/v1/provider/customers \
-H "x-provider-key: mf_..." \
-H "content-type: application/json" \
-d '{
"customers": [
{ "domains": ["example.com", "example-group.com"], "reference": "CRM-4417", "seats": 5 },
{ "domains": ["sample-ltd.com"], "reference": "CRM-4418", "seats": 2 }
]
}' The response counts newly added clients under added and all others under unchanged, even if their seats changed.
{
"added": 1,
"unchanged": 1,
"warnings": [{ "domain": "sample-ltd.com", "reason": "tenant_not_resolvable" }]
} 5. Warnings
A faulty domain does not hold up the rest of the list. Every domain that is not taken over as sent is listed under warnings with a reason.
tenant_not_resolvable: no Microsoft 365 organisation is behind the domain yet, or the domain is invalid. Valid domains are stored and checked again regularly. As soon as the organisation is found, the client becomes active.
own_tenant: the domain is not taken over.
multiple_organisations: the domains of one entry belong to several organisations. The first one in your list gets the seats, the others get none. Set their seats with the call in the next section.
store_failed: the entry could not be saved. Send it again.
6. Setting a client’s seats
PUT /customers/{id}/seats sets the number of seats for a client.
curl -X PUT https://app.flow.jumoca.de/v1/provider/customers/{id}/seats \
-H "x-provider-key: mf_..." \
-H "content-type: application/json" \
-d '{ "seats": 8 }' The response is the client as it now stands. status is active, pending_resolution while no organisation has been found, or removed.
{
"id": "66f1...",
"domains": ["example.com", "example-group.com"],
"reference": "CRM-4417",
"status": "active",
"seats": 8,
"removalScheduledAt": null,
"addedAt": "2026-09-02T08:14:03.000Z"
} 7. Removing clients
DELETE /customers/{id} removes a client and responds with 204. The client’s access does not end immediately, removalScheduledAt gives the date. If you send the client again with POST /customers before then, the removal is cancelled.
curl -X DELETE https://app.flow.jumoca.de/v1/provider/customers/{id} \
-H "x-provider-key: mf_..." 8. Errors
Errors come with an HTTP status and a fixed code in the body.
400 invalid_json or invalid_body: the request is not valid JSON or does not have the format described here. details.issues names the affected fields.
401 unauthorized: the key is missing, wrong or revoked, or the call needs a signed-in person.
403 account_closed: your account is closed.
404 customer_not_found: there is no client with this ID.
429 rate_limited: the request limit has been reached. Try again after the number of seconds in details.retryAfterSeconds.
503 billing_sync_failed: billing could not be updated and nothing has changed. Send the call again a little later.
{ "error": { "code": "customer_not_found", "message": "Customer not found" } }