Make OAuth and Automation API

Keito’s Make connector is in controlled beta; Make review is pending. It is not yet a public directory listing. Workspace enablement, current plan/subscription eligibility, active membership, and the connected person’s permissions all apply. See Make setup and modules for installation and scenario guidance.

This guide describes the API contract used by the connector. Use the OpenAPI specification for field schemas and the endpoint index for the wider API. A general API key does not become a Make OAuth connection by supplying a header.

OAuth and workspace binding

The connector uses OAuth 2.0 authorization code through Keito’s identity provider, with openid profile email offline_access scopes and client_secret_basic token authentication. Make manages the authorization redirect and refresh tokens. These identity scopes do not grant unrestricted Keito data access; current role permissions and the Make route allowlist are checked on requests.

Authenticated connector requests send:

Authorization: Bearer <access-token>
Keito-Make-Connection: <stable-connection-key>
Keito-Account-Id: <company-id>

The connector generates the binding key once during token exchange and preserves it on refresh. It must contain 16–128 letters, digits, dots, underscores, tildes, or hyphens. Treat it as connection data, not a value to map from a scenario.

GET /api/v2/users/me bootstraps the connection and reports make_access with enabled, reason, message, and connection_bound. It can report an access refusal without granting access to other resources. It still requires valid authentication and account context; do not assume every valid token receives 200.

The first request carrying a valid binding key, including bootstrap, pins the user/client/key to a workspace even if the bootstrap reports Make access disabled. The initial bootstrap may select an explicit Keito-Account-Id, or the active workspace when omitted. In Make, Workspace ID (optional) accepts the Company ID from Settings > API & Developers > Company ID. The connector stores the returned company ID and sends it on later requests. Changing the active Keito workspace does not move the binding. A later mismatch returns 409 workspace_mismatch with bound_company_id; create another connection instead.

Method Endpoint Purpose
GET /api/v2/integrations/make/connections/current Read the existing calling connection’s workspace, status, and timestamps
DELETE /api/v2/integrations/make/connections/current Revoke the calling binding and active bindings sharing its consent

Both endpoints require a Make-origin OAuth credential and an existing binding belonging to that user/client. They remain usable for connection management when rollout or the write switch blocks ordinary operations; they do not create a new binding. An unknown binding returns 404 make_connection_not_found; other credential origins receive 403 make_origin_required. Repeating DELETE is safe. Local revocation takes effect independently of best-effort provider grant revocation, which is attempted when no active Make binding for that user/client remains. The response reports the outcome; never infer successful provider revocation from local revocation alone.

Supported routes

The allowlist checks both method and path. Other public API operations are not automatically available to Make.

Method API v2 path Use
GET /api/v2/users/me Bootstrap and connection information
GET, DELETE /api/v2/integrations/make/connections/current Connection management
GET, POST /api/v2/clients, /api/v2/contacts Watch, search, create, find or create
GET, POST /api/v2/projects Watch, search, create, find or create
GET /api/v2/projects/{id}, /api/v2/project_templates Project/template lookups
GET, POST /api/v2/tasks Task search and creation
GET, POST /api/v2/projects/{projectId}/task_assignments Read or create assignments
PATCH /api/v2/projects/{projectId}/task_assignments/{id} Update assignment budget, rate, or billable status
GET /api/v2/projects/{projectId}/user_assignments, /api/v2/users People lookups
GET, POST /api/v2/time_entries, /api/v2/expenses Watch and create
GET /api/v2/expense_categories, /api/v2/expenses/{id}/receipt Expense lookups and receipt access
GET, POST /api/v2/invoices Watch, search, create, find or create
POST /api/v2/invoices/{id}/messages Send, reminder, or thank-you email
POST /api/v2/estimates Create a draft estimate

In Make an API Call, use a relative path such as /clients and separate query fields. Authentication, company, and connection headers are supplied by the connector and cannot be overridden. Redirects are not followed. A local guard rejection returns statusCode: 0 and body.error: request_rejected without an HTTP request; a server allowlist refusal is 403 make_route_not_allowed.

Polling and cursors

The six watched collections are /api/v2/clients, /api/v2/contacts, /api/v2/projects, /api/v2/time_entries, /api/v2/expenses, and /api/v2/invoices.

Parameter Contract
sort_by created_at or updated_at; enables polling mode
sort_order desc only; asc is rejected
created_since, created_before Inclusive lower and exclusive upper bounds when sorting by creation time
updated_since, updated_before Inclusive lower and exclusive upper bounds when sorting by update time
per_page API maximum 2,000 without includes; 200 with an include expansion. Connector Watch modules request 300 per page
cursor Opaque next_cursor from the preceding response; use unchanged

Use ISO 8601 timestamps with millisecond precision. The upper bound defaults to, and is capped at, server time minus 30 seconds. Match the *_before field to the sort field; updated_since can also act as an additional filter under creation sorting. Without sort_by, routes retain their normal page-number contract. Make-origin requests reject unknown parameters and polling-only parameters without sort_by; blank values are treated as absent.

The response retains the collection and pagination envelope and adds next_cursor (null at the end). total_entries counts the bounded window, page is the cursor’s page counter, and total_pages is the ceiling of window count divided by page size. Descending keyset order uses timestamp and record ID. Keep the resource, workspace, user, sort, filters, and page size consistent throughout a walk. If the window size is an exact multiple of the page size, a final empty page can have page = total_pages + 1; continue until next_cursor is null rather than relying on page count alone.

Cursors expire after 24 hours and are bound to the resource, workspace, user, sort field, and filters. 400 cursor_invalid, cursor_expired, or cursor_mismatch requires restarting the walk without the cursor, retaining the intended time window. Do not interpret a failed page as an empty collection.

Connector Watch modules freeze a scan end, use the API’s settle delay, and refuse windows over 3,000 records with poll_backlog_exceeded before emitting or checkpointing. Their output limit is 1–300 bundles, default 100. Updated invoices expose event_id = <id>:<updated_at>; intermediate edits between polls are coalesced. These are scheduled snapshots, not an audit event stream or an exactly-once delivery guarantee.

Search and includes

Collection Search/filter extensions
Clients name, match=exact|contains, is_active
Projects name, match, client_id, is_active, include=people
Tasks name, match, project_id, is_active
Contacts client_id, email (exact, case-insensitive)
Invoices number (exact, case-insensitive; draft placeholders do not match), state, client_id
Time entries client_id, project_id, user_id (me accepted), include=client,client_contacts,invoice_recipients

Name matching trims and case-folds the input. contains uses a literal substring and ranks exact matches first unless polling sort overrides that order. The connector’s name searches restrict to active records. Search modules emit zero bundles for no match; find-or-create modules instead return one {created, matched_by, entity} bundle or raise ambiguous_match / not_found. Their search and create steps are separate requests, not an atomic uniqueness constraint.

Includes are opt-in and loaded once per relation per page. Any include limits per_page to 200. Contact expansions require contact permission and fail with 403 contacts_not_visible rather than silently returning no contacts. invoice_recipients contains recipient-status contacts with nonblank email. Project people have email: null when the caller cannot list users. See time entries, projects, and the schema for response fields.

Mutations and idempotency

Use an Idempotency-Key matching ^[A-Za-z0-9._-]{8,255}$. Keys are scoped to workspace and user, and fingerprint the method, route, and raw body. Use a separate stable key per source event and action. Retention is 24 hours. An identical retry replays the stored status/body with Idempotency-Replayed: true, after current authorization checks. Stored results can include errors after a committed write; a pre-write refusal releases the claim.

The connector requires keys for invoice creation, invoice messages, expenses, estimates, and all four find-or-create modules. Other dedicated actions expose an optional key. For a universal-module mutation, add the header yourself. A find-or-create key protects only its create request; a repeat search can find an existing entity first. It does not make names unique or serialize separate scenarios.

Response Recovery
400 malformed key Correct the key format before an operation is accepted
409 conflict describing a reused key or changed authorization Keep the key and reconcile the original request; do not rotate it to force a write
409 conflict describing an in-progress/being-claimed request Allow retry/backoff with the same key
409 idempotency_unresolved A claim older than five minutes has no completed receipt; inspect the record and reconcile before considering a new operation

Read the message as well as the code: several conflict causes share error: conflict. After 24 hours, a repeated key can represent a new operation, so reconcile older uncertain writes before retrying.

POST /api/v2/invoices/{id}/messages with event_type=send issues a draft and attempts email delivery in the same request. A committed 400 stating that issuance succeeded but delivery failed is stored under the key. Inspect the invoice and delivery state; a new key could send another message. Find or Create an Invoice deliberately does not apply the searched final number to its new draft: retain the returned draft ID for later work.

Time-entry and expense actions use source: "api" and can send JSON-object metadata (maximum 4 KB); the connector adds module metadata where supported. Requests have a 128 KB JSON body limit. Invoice line-item ordering follows array position. Consult the resource guide before changing financial state.

Errors and limits

Errors include error, error_description, and message; Make access refusals also include reason.

Status / code Meaning
412 make_access_denied, UNCONFIGURED Environment configuration is missing
403 make_access_denied Rollout/beta, plan/subscription, suspension, schema, user, membership, or actor eligibility refused access
401 make_access_denied, BINDING_REVOKED Reconnect after resolving the revoked authorization
400 make_connection_required Required connection binding header is missing or invalid
409 workspace_mismatch Connection is pinned to another company; response includes bound_company_id
409 make_connection_limit Ten active bindings per user and OAuth client
403 make_writes_disabled Writes paused; ordinary reads remain available
403 make_route_not_allowed Path/method outside Make’s allowlist
429 Rate limited; respect Retry-After

The default API rate limit is 600 requests per minute per OAuth token credential, keyed by token fingerprint rather than Make connection. /api/v2/tasks has a default 120/minute per-credential limit and an additional 120/minute service-wide shared limit. These defaults are configurable. Normal page sizes remain resource-specific outside polling mode. Reduce schedule frequency or unnecessary lookups on rate limiting, use the supplied retry delay, and keep mutation keys stable. A successful bootstrap or healthy API response is not proof of workspace enablement or public Make approval.