OAuth Connections
OAuth authorization flow and connection management.
GET /api/integrations/{id}/connect
Initiate the OAuth authorization flow.
Authentication: Required
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Integration id or slug |
Response:
Redirects to the provider's authorization page (driver-dependent). The server generates and later validates the OAuth state parameter for CSRF protection.
OAuth flow:
- The client calls
GET /api/integrations/{id}/connect. - The server redirects to the provider's authorization page.
- The user authorizes on the provider's site.
- The provider redirects back to
GET /oauth2/callback?code=...&state=.... - The server exchanges the code for tokens and stores the connection.
POST /api/integrations/{id}/connect
Complete the connection by exchanging an authorization code (called by the callback). The body carries the provider's query (the code/state returned to the callback).
Authentication: Required
Response:
Renders the OAuth success page (HTML). The connection is created as a side effect.
GET /oauth2/callback
OAuth callback endpoint, invoked by the provider's redirect. Not called directly by clients.
Authentication: Not required (public endpoint)
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
code | string | Authorization code from the provider |
state | string | State parameter for CSRF validation |
error / error_description | string | Set when authorization failed |
Response:
Redirects to a success or error page.
GET /api/integrations/{id}/connection
Get the current connection for an integration.
Authentication: Required
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Integration id or slug |
Response:
Token/refresh expiry and last-authorization timestamps. The refresh token itself is never returned; a hasRefreshToken boolean indicates whether one is stored. Responds 404 when there is no connection. For per-user integrations this is the caller's connection; for shared integrations, the shared one.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/integrations/00000000-0000-4000-8000-000000000001/connection"
}
},
"id": "00000000-0000-4000-8000-000000000002",
"tokenExpiresAt": "2026-01-15T16:20:00.000Z",
"refreshTokenExpiresAt": "2026-01-15T16:20:00.000Z",
"lastAuthorizedAt": "2026-01-15T16:20:00.000Z",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"ownerId": "admin",
"integrationId": "00000000-0000-4000-8000-000000000001",
"integration": {
"naturalId": "admin:acme-crm",
"driver": {
"id": "api",
"editorComponent": "apiIntegrationEditor",
"extraAuthUriParams": {},
"supportsUpload": false
},
"iconUrl": "/images/icons/plug.svg",
"permissions": {
"write": true,
"edit": true,
"delete": true,
"share": true,
"assignTags": true,
"changeOwner": true
},
"id": "00000000-0000-4000-8000-000000000001",
"name": "Acme CRM",
"slug": "acme-crm",
"description": null,
"category": null,
"type": "api",
"mode": "per-user",
"authType": "apiKey",
"basicTokenAuth": null,
"data": null,
"embedded": false,
"clientId": null,
"clientSecret": null,
"apiKey": "encrypted:iv:0000000000000000",
"apiKeyLocation": "header",
"apiKeyName": "X-API-Key",
"headers": {},
"scope": null,
"apiBaseUri": "https://api.acme.example.com/v1",
"authUri": null,
"tokenUri": null,
"revocationUri": null,
"imageUrl": null,
"libraryId": null,
"mailTenantId": null,
"ownerId": "admin",
"shared": false,
"folderId": null,
"source": null,
"sourceId": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme",
"icon": null
},
"hasRefreshToken": true
}
DELETE /api/integrations/{id}/connection
Disconnect and revoke the connection.
Authentication: Required
Permissions: The caller must own the connection (or be a superuser)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Integration id or slug |
Response:
Revokes the tokens with the provider (when revocationUri is configured) and deletes the connection. Responds 200 with an empty body.
Disconnecting may break libraries, jobs, or other features that depend on the integration until it is reconnected.
GET /api/integration-connections
List the caller's connections across all integrations.
Authentication: Required
Response:
A HAL collection under _embedded["inf:connection"], each connection embedding its integration. Always scoped to the caller: even a superuser or impersonator sees only their own connections (and an impersonator sees none).
{
"_links": {
"self": {
"href": "https://informer.example.com/api/integration-connections"
}
},
"start": 0,
"count": 1,
"total": 1,
"_embedded": {
"inf:connection": [
{
"_links": {
"self": {
"href": "https://informer.example.com/api/integrations/00000000-0000-4000-8000-000000000001/connection"
}
},
"id": "00000000-0000-4000-8000-000000000002",
"integrationId": "00000000-0000-4000-8000-000000000001",
"ownerId": "admin",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"_embedded": {
"inf:integration": {
"_links": {
"self": {
"href": "https://informer.example.com/api/integrations/00000000-0000-4000-8000-000000000001"
},
"inf:integration-share": {
"href": "https://informer.example.com/api/integrations/00000000-0000-4000-8000-000000000001/shares/{principalId}",
"templated": true
},
"inf:icon": {
"href": "https://informer.example.com/api/integrations/00000000-0000-4000-8000-000000000001/icon"
},
"inf:connect": {
"href": "https://informer.example.com/api/integrations/00000000-0000-4000-8000-000000000001/connect"
},
"inf:connection": {
"href": "https://informer.example.com/api/integrations/00000000-0000-4000-8000-000000000001/connection"
},
"inf:request": {
"href": "https://informer.example.com/api/integrations/00000000-0000-4000-8000-000000000001/request"
},
"inf:favorite": {
"href": "https://informer.example.com/api/integrations/00000000-0000-4000-8000-000000000001/favorite"
},
"inf:integration-owner": {
"href": "https://informer.example.com/api/integrations/undefined%3Aacme-crm/owner"
},
"inf:integration-shares": {
"href": "https://informer.example.com/api/integrations/undefined%3Aacme-crm/shares"
}
},
"naturalId": "undefined:acme-crm",
"driver": {
"id": "missing",
"extraAuthUriParams": {},
"supportsUpload": false
},
"iconUrl": "/images/icons/plug.svg",
"permissions": {
"write": true,
"edit": true,
"delete": true,
"share": true,
"assignTags": true,
"changeOwner": true
},
"id": "00000000-0000-4000-8000-000000000001",
"name": "Acme CRM",
"slug": "acme-crm",
"description": null,
"imageUrl": null,
"icon": null
}
}
}
]
}
}
POST /api/integrations/{id}/request
Proxy an authenticated request through an integration. The integration applies its stored credentials (OAuth tokens, API key, etc.) and refreshes tokens as needed.
Authentication: Required
Permissions: A valid connection (for connection-based auth types)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Integration id or slug |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Relative (combined with apiBaseUri) or same-origin absolute URL |
method | string | No | GET (default), POST, PUT, PATCH, DELETE, HEAD, OPTIONS |
headers | object | No | Additional request headers |
params | object | No | URL query parameters |
data | any | No | Request body |
encoding | string | No | base64 to send a binary data body (decoded before sending) |
Example:
{ "method": "GET", "url": "/files", "params": { "pageSize": 10 } }
Response:
The proxied response from the external API, streamed back with its status and headers. (Calls the external service, so it is not exercised in the captured examples.)
To prevent SSRF and credential exfiltration, an absolute url is accepted only when it shares the integration's configured origin and falls under its base path. Otherwise use a relative URL.
GET /api/integrations/{id}/request/{path*}
Read-only (GET) proxy variant addressed by path, safe for whitelisted external link sharing.
Authentication: Required
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Integration id or slug |
path | string | Relative API path (query string is forwarded) |
Example:
GET /api/integrations/admin:acme-crm/request/files?pageSize=10
Response:
The proxied response from the external API.