Skip to content

Google Business connections

A connection is one Google identity — a Google account or an agency manager account — that you (or one of your own clients, through the hosted connect flow) authorized. It is what holds the permission and what you can revoke; its businesses (locations) are listed beneath it. You can also create a new business under a connection (below).

Lists your connections. It reads MauKirim’s own table: no Google call and no budget.

Scope read or gmb
Auth Authorization: Bearer mk_live_…
Pagination none
Query param Type Meaning
externalId string Only the connections made for one of your own clients — your id for them, as you passed it when creating a connect session. An empty value (?externalId=) keeps only the connections you made yourself
Terminal window
curl https://app.maukirim.com/api/v1/gmb/connections \
-H "Authorization: Bearer mk_live_…"
const response = await fetch("https://app.maukirim.com/api/v1/gmb/connections", {
headers: { Authorization: `Bearer ${process.env.MAUKIRIM_KEY}` },
})
const { connections } = await response.json()
$curl = curl_init('https://app.maukirim.com/api/v1/gmb/connections');
curl_setopt_array($curl, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('MAUKIRIM_KEY')],
CURLOPT_RETURNTRANSFER => true,
]);
$connections = json_decode(curl_exec($curl), true)['connections'];
{
"ok": true,
"connections": [
{
"id": "gmc_2m9dq7zk0p",
"label": "Agency account",
"googleEmail": "agency@example.com",
"externalId": "",
"status": "active",
"lastError": null,
"lastSyncedAt": "2026-10-14T01:05:00.000Z",
"createdAt": "2026-10-14T01:04:30.000Z",
"locationCount": 3
}
]
}
Field Meaning
id The connection id (gmc_…); filter GET /gmb/locations with connectionId
label The name you gave it when connecting
googleEmail The Google account, for display
externalId Your id for the end user who connected it through the hosted flow, or "" for a connection you made yourself
status active, needs_reauth (Google refused the permission; reconnect in the dashboard), revoked (you disconnected it) or suspended (switched off by an operator)
lastError A short code for the last failure, or null
lastSyncedAt When its businesses were last read from Google
locationCount Businesses under it that are switched on. Businesses a connect or sync finds arrive switched off and cost no allowance; turn on the ones you want

The response never contains a token, the saved permission or any Google account identifier beyond the email address.

There is no API endpoint that connects a Google account for you, on purpose: connecting needs the owner of the Google account to approve Google’s consent screen in a browser. An API key cannot do that, and neither can an MCP assistant. There are two ways to get a connection:

Reconnecting after needs_reauth is also the owner’s act, in the dashboard (or a new connect session for a client).


Creates a new business on Google under this connection’s Google account and imports it into MauKirim at once. 201 Created. Use it to open a branch for a client, or to put a business that has no listing yet on Google.

Scope gmb
Path id — the connection id (gmc_…)
Header Idempotency-Key (optional, strongly recommended): 8–128 characters of letters, digits and _ . : -. It is passed to Google as the request id, so a retry can never create a second business
Field Required Rule
title yes The business name, 1–100 characters
categories yes 1–10 category resource names such as categories/gcid:coffee_shop. The first is the primary category. Find names with categories
address yes An object: regionCode (required, two letters such as ID), addressLines (required, 1–5 lines of text), and optional locality (city), postalCode, administrativeArea (province or state), languageCode
phone no Up to 32 characters, digits and + ( ) . - space, starting with + or a digit
websiteUri no An absolute http or https URL, up to 2000 characters
description no Up to 750 characters
languageCode no The business’s language, for example id
regularHours no Google’s regularHours shape ({ "periods": [ … ] })
accountName only if needed accounts/123. Needed only when the Google identity manages several Business Profile accounts; leave it out otherwise. Without it, and with several accounts, the call is 400 invalid_input and the message lists them
validateOnly no true has Google check the listing and creates nothing (see below)

Any other field is 400 invalid_input. MauKirim checks the whole listing before it calls Google, so an invalid one spends no budget.

Check first with validateOnly. "validateOnly": true asks Google to validate the listing exactly as it would for a real create, and nothing is created at Google or in MauKirim. The answer is { "ok": true, "replayed": false, "validated": true, "location": null }. A listing Google would refuse comes back as 400 invalid_input. A validation does not count against your business allowance.

Terminal window
curl -X POST https://app.maukirim.com/api/v1/gmb/connections/gmc_2m9dq7zk0p/locations \
-H "Authorization: Bearer mk_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-client-17-branch-2" \
-d '{
"title": "Kopi Baru",
"categories": ["categories/gcid:coffee_shop", "categories/gcid:cafe"],
"phone": "+62 21 5550 123",
"websiteUri": "https://kopibaru.example",
"address": { "regionCode": "ID", "locality": "Jakarta", "postalCode": "10110", "addressLines": ["Jl. Melati 1"] }
}'
const response = await fetch(`https://app.maukirim.com/api/v1/gmb/connections/${connectionId}/locations`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.MAUKIRIM_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "create-client-17-branch-2",
},
body: JSON.stringify({
title: "Kopi Baru",
categories: ["categories/gcid:coffee_shop"],
address: { regionCode: "ID", locality: "Jakarta", postalCode: "10110", addressLines: ["Jl. Melati 1"] },
}),
})
const { location } = await response.json()
$curl = curl_init("https://app.maukirim.com/api/v1/gmb/connections/$connectionId/locations");
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('MAUKIRIM_KEY'),
'Content-Type: application/json',
'Idempotency-Key: create-client-17-branch-2',
],
CURLOPT_POSTFIELDS => json_encode([
'title' => 'Kopi Baru',
'categories' => ['categories/gcid:coffee_shop'],
'address' => ['regionCode' => 'ID', 'locality' => 'Jakarta', 'postalCode' => '10110', 'addressLines' => ['Jl. Melati 1']],
]),
CURLOPT_RETURNTRANSFER => true,
]);
$location = json_decode(curl_exec($curl), true)['location'];
{
"ok": true,
"replayed": false,
"validated": false,
"location": {
"id": "gml_4t7nw1c9de",
"connectionId": "gmc_2m9dq7zk0p",
"title": "Kopi Baru",
"addressSummary": "Jl. Melati 1, Jakarta 10110",
"primaryCategory": "Coffee shop",
"phone": "+62 21 5550 123",
"websiteUri": "https://kopibaru.example/",
"placeId": "ChIJ…",
"verificationState": "unverified",
"status": "active",
"lastSyncedAt": "2026-10-14T03:10:00.000Z"
}
}

The location is the same object GET /gmb/locations/{id} returns, with an id you can use in every other call straight away. Repeating the request with the same Idempotency-Key returns the stored answer with "replayed": true and creates no second business; the same key with a different body is 409 gmb_idempotency_conflict.

It is created switched on, counts against your allowance, and is refused first. (Unlike a business found by a sync, which arrives off and costs nothing until you choose it.) Your package allows a fixed number of active businesses (see Google Business Profile). When you are at the limit the call is 409 gmb_location_limit before anything is created on Google, so you never end up with a business that exists on Google but that MauKirim will not show. Disconnect or delete a business, or change package, to make room. A validation is not refused for the limit.

A new business is not verified. Google shows a freshly created business as unverified: many of its public changes only take effect once it is verified. Continue with the verification endpoints (options, start, then enter the code), and add its photos and posts afterwards.

The call costs one Google call, plus one more to find the Google account when you leave out accountName (see rate limits). Google may need approval for the Business Information API in the deployment’s Google project; if it is not enabled the answer is 503 gmb_api_unavailable.

HTTP Code Condition
400 invalid_input A field that cannot be set, a missing or invalid title, categories or address, an invalid phone, website or accountName, several Google accounts and no accountName, a malformed Idempotency-Key, or Google rejected the listing
402 gmb_plan_required The package does not include the connector
403 api_key_scope_denied The key lacks gmb
403 gmb_forbidden Google says this connection may not create businesses (or it manages no Business Profile account)
404 gmb_connection_not_found No such connection, or it is not this account’s. Identical for both
409 gmb_location_limit The package’s business allowance is full; nothing was created
409 gmb_reconnect_required / gmb_idempotency_conflict The connection needs reconnecting; the key was used with a different body
429 gmb_rate_limited No budget now (Retry-After); also an identical request with the same key still running
502 gmb_upstream_error Google failed
503 gmb_api_unavailable The Business Information API is not enabled or approved for the deployment’s Google project

Disconnects one connection: this is how you offboard a client.

Scope gmb
Path id — the connection id (gmc_…)

MauKirim asks Google to forget the saved permission (best effort: a Google outage never stops you leaving), marks the connection revoked, and switches every business under it to disabled, so they stop counting against your allowance. History and usage stay, and the public listings themselves are not touched. Read calls on the stored copy of a disabled business still work; anything that would call Google answers gmb_location_disabled.

Terminal window
curl -X DELETE https://app.maukirim.com/api/v1/gmb/connections/gmc_2m9dq7zk0p \
-H "Authorization: Bearer mk_live_…"
const response = await fetch(`https://app.maukirim.com/api/v1/gmb/connections/${connectionId}`, {
method: "DELETE",
headers: { Authorization: `Bearer ${process.env.MAUKIRIM_KEY}` },
})
$curl = curl_init("https://app.maukirim.com/api/v1/gmb/connections/$connectionId");
curl_setopt_array($curl, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('MAUKIRIM_KEY')],
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($curl), true);
{ "ok": true }

To bring the client back, create a new connect session with the same externalId: the same Google account for the same externalId reuses the connection and its businesses come back.

HTTP Code Condition
401 api_key_invalid / api_key_revoked / api_key_expired
402 gmb_plan_required The package does not include the connector
403 api_key_scope_denied (GET) Neither read nor gmb; (DELETE) no gmb; forbidden for an inactive account
404 gmb_connection_not_found (DELETE) No such connection, or it is not this account’s. Identical for both
500 server_error Unexpected