Skip to main content
Uses: Python · TypeScript · CLI · REST API
If you’re embedding Engini in your own product, this is the flow you have to implement: your user clicks “Connect Gmail”, authorizes in their browser, and comes back with a working connection - never seeing Engini. Four steps: discover the auth method, get a sign-in URL, poll for the captured token, create the connection.
cURL samples assume BASE=https://api.engini.io/v1 and AUTH="x-api-key: $ENGINI_API_KEY" - the setup from the REST walkthrough.

1. Find the OAuth method

Each application exposes one or more authentication methods. Prefer the one flagged isDefault - exactly one method is marked default, and it’s the one used when a connection-create request omits authenticationId - rather than picking “the first one with requiresOAuthSignIn”. Methods also carry a description and, when set, a helpUrl (a per-method install-guide link); show both if you’re building your own picker.
Some applications require values before sign-in (a Salesforce sandbox flag, a region). Those are the connectionFields entries with isRequiredOnSignin: true - collect them from your user first and pass them as fields in the next step.

2. Get a sign-in URL

Send your user to signInUrl (redirect, popup, or new tab). Keep the state - it’s how you collect the result. Store it against the user’s session.

3. Poll until they finish

Engini captures the provider’s callback for you. Poll with the state until a token appears.
While the user is still authorizing, this endpoint returns 200 with an empty body - not a 404 and not an error. Treat “no body” as “keep polling”, and always bound the loop with a timeout so a user who abandons the tab doesn’t hang your request.

4. Create the connection

Pass the sign-in’s state as oauthState - the server looks up its own record of the sign-in and derives the connection’s credentials itself.
Don’t build fields from connectionData - values you send for server-derived keys (ApiKey, RefreshToken) are ignored, and ExternalOrgId is rejected outright with 400 INVALID_FIELD. Omitting oauthState on an OAuth2 method returns 400 OAUTH_STATE_REQUIRED; a state the server can’t resolve - expired past 5 minutes, unknown, or another account’s - returns 400 OAUTH_STATE_EXPIRED, deliberately identical for all three so it isn’t an existence oracle. This applies to OAuth2 methods only; OAuth2WithoutSignIn (client-credentials) connectors need no state.
Requires engini / @engini/sdk / @engini/cli 0.21.0 or newer (0.20.0 and earlier cannot create OAuth2 connections).
Setting a default matters more than it looks: with one, tool calls can omit connectionId entirely and still resolve. Without one - and with more than one connection for that app - execution fails with 409 NO_DEFAULT_CONNECTION.

Doing it without writing any of this

The CLI implements this whole flow, including the agent-resumable variant:

Next