Integrations

Email and calendar connections

Connect user-owned Gmail and Outlook accounts — mailbox and calendar — without handling provider tokens.

Deeplinq treats personal mailbox access as a user-owned connection. Your organization registers its OAuth application, then each end user completes the provider consent flow through the engine.

Supported providers:

  • Gmail;
  • Outlook through Microsoft Graph.

Both providers expose the same governed tool vocabulary:

ToolPurpose
email.searchSearch with the portable Deeplinq email query grammar
email.readRead a message and list its attachments
email.attachment.downloadDownload one bounded attachment
email.sendSend text, HTML, multipart content, and bounded attachments
calendar.listList events in a time window on the primary calendar
calendar.createCreate one event
calendar.updateChange the named fields of one event
calendar.deleteDelete one event

The calendar tools need the calendar scopes on the integration (below). An integration registered without them keeps its mail tools; a calendar call on such a connection answers a 403 asking the user to reconnect, which re-runs consent with the new scopes.

Prerequisites

  • set PUBLIC_URL to the externally reachable HTTPS engine URL;
  • create an OAuth application with Google Cloud or Microsoft Entra;
  • keep the client secret available to the organization administrator;
  • choose one or more HTTPS return URL prefixes in the partner application.

Deeplinq computes a fixed callback from PUBLIC_URL. Register the redirect_uri returned by the integration API in the provider console.

Ownership boundary

A connection belongs to the user who authorized it. Organization administrators may inspect connection metadata and revoke a connection, but they cannot invoke mailbox tools on behalf of another user.

Register an OAuth application

An organization administrator registers a named integration. Multiple apps per provider are supported for environments or brands; names are unique per organization and provider.

Gmail:

curl -H "Authorization: Bearer $ORG_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -X POST "$BASE_URL/v1/integrations" -d '{
  "provider":"gmail",
  "name":"Production",
  "client_id":"<google-client-id>",
  "client_secret":"<google-client-secret>",
  "scopes":[
    "openid",
    "email",
    "https://www.googleapis.com/auth/gmail.readonly",
    "https://www.googleapis.com/auth/gmail.send",
    "https://www.googleapis.com/auth/calendar.events",
    "https://www.googleapis.com/auth/gmail.send"
  ],
  "return_url_prefixes":["https://app.example.com/settings/connections"]
}'

Outlook requires its complete seven-scope catalog:

[
  "openid",
  "profile",
  "email",
  "offline_access",
  "User.Read",
  "Mail.Read",
  "Mail.Send"
]

The client secret is sealed and never returned. PATCH /v1/integrations/{id} can rename, activate/deactivate, change scopes or return prefixes, and rotate the client secret.

Start authorization

Your trusted backend requests an authorization URL for the signed-in user:

curl -H "Authorization: Bearer $DEEPLINQ_TOKEN" \
  -H "Content-Type: application/json" \
  -X POST "$BASE_URL/v1/connections/authorize" -d '{
  "provider":"outlook",
  "integration_id":"<integration-id>",
  "return_url":"https://app.example.com/settings/connections/complete"
}'

Redirect the browser to the returned URL. The engine owns OAuth state, PKCE, the provider callback, token exchange, refresh, sealing, and return URL validation.

The partner application never receives a Google or Microsoft access token.

When exactly one active integration exists for a provider, integration_id may be omitted. With multiple active integrations it is required; Deeplinq returns 409 instead of guessing.

List and revoke connections

GET    /v1/connections?limit=50&cursor=<opaque>
GET    /v1/connections/{connection_id}
DELETE /v1/connections/{connection_id}

The owner sees connection metadata, not tokens. An organization administrator can inspect and revoke connection metadata across the organization through the admin console, but cannot invoke tools as that user.

Invoke a tool

After authorization, the connection owner can inspect its tool catalog and invoke a supported operation:

GET /v1/connections/{connection_id}/tools
POST /v1/connections/{connection_id}/invoke
{
  "tool": "email.search",
  "args": {
    "query": "from:billing@example.com has:attachment",
    "max_results": 10
  }
}

query is optional. Omit it (or send an empty string) to list the mailbox's newest messages first, with no filter — {"max_results": 1} is "the newest message". Otherwise the portable query grammar supports bare terms, quoted phrases, from:, to:, cc:, subject:, body: (each with a value, e.g. from:ada@example.com), after:YYYY-MM-DD, before:YYYY-MM-DD, and has:attachment. Whitespace means AND; repeating a qualifier means OR. Provider-native Gmail or Exchange operators (in:, is:, label:, folder names) are rejected, and the refusal names the supported set.

Calendar tools

Events live on the connected account's primary (default) calendar. Instants are RFC 3339 with an offset and are never moved; time_zone (an IANA name) only chooses how the calendar displays the event.

{
  "tool": "calendar.create",
  "args": {
    "title": "Flight LH1234 Paris CDG → New York JFK",
    "start": "2026-10-05T09:30:00+02:00",
    "end": "2026-10-05T18:15:00+02:00",
    "time_zone": "Europe/Paris",
    "location": "Paris Charles de Gaulle (CDG), Terminal 1",
    "description": "Booking reference ABC123"
  }
}

calendar.list takes time_min and time_max (at most one year apart), an optional query matched against title, location and description, and pages with next_page_token/page_token. calendar.update changes only the fields it names; calendar.delete takes the event_id.

Scopes to register: Google https://www.googleapis.com/auth/calendar.events (or calendar.events.readonly for calendar.list alone); Microsoft Calendars.ReadWrite (or Calendars.Read). Outlook's mail scopes stay required; the calendar scopes are optional additions.

In an agent's reach, calendar.list runs unattended and the three writes stop for the person's approval, exactly like email.send. calendar.create is idempotent under the engine's retry: the same tool call re-creates the same event (Google event id, Graph transactionId), never a duplicate.

Worked example: travel confirmations into calendar blocks

Parsing an itinerary is prompt work in the agent's instructions, not engine code. A persona that turns flight and hotel confirmations into events:

You manage the person's calendar from their mailbox.

When asked to add a trip: search the mailbox for the confirmation (airline,
booking reference or hotel name), read the message, and create ONE event per
flight segment and ONE per hotel stay.

For a flight: title "Flight <number> <origin IATA> → <destination IATA>",
start = scheduled departure, end = scheduled arrival, location = departure
airport and terminal, description = booking reference and seat. Convert the
local times the email gives into RFC 3339 instants WITH their offsets
(09:30 CEST is 09:30+02:00; 12:15 EDT is 12:15-04:00) and set time_zone to
the departure city's zone.

For a hotel: title "Hotel <name>", start = check-in day at 15:00 local unless
the email says otherwise, end = check-out day at 11:00 local, location = the
hotel address.

Before creating, list the calendar for that day and do not create an event
that already exists with the same title and start. Never invent a time the
email does not state; if a time is missing, say so instead of guessing.

The engine gates each calendar.create behind approval, so the person sees the exact event before it lands.

Limits and delivery safety

  • search uses opaque provider-bound cursors;
  • read bodies are bounded and may be truncated;
  • send allows at most 25 combined to and cc recipients;
  • send allows at most 5 attachments and 3 MiB decoded attachment data;
  • attachment downloads are membership-checked and capped at 3 MiB;
  • oversized tool results are rejected, never silently truncated;
  • ambiguous Gmail send retries fail as tool_uncertain;
  • Outlook send does not retry ambiguous delivery, preventing silent duplicates;
  • calendar writes whose outcome cannot be proven fail as a conflict and are never retried blind;
  • calendar events span at most 31 days; a list window at most one year; at most 25 attendees.

Agent grants can expose read tools with auto policy and gate email.send, calendar.create, calendar.update and calendar.delete with requires_approval — which is what the engine does on its own for a task run from a request.

On this page