OAuth connectors
Connect providers like Stripe and X with a consent flow instead of pasted credentials.
Some providers are connected by authorizing an account rather than pasting keys. The flow is the standard browser dance: you request an authorization URL, the user consents in a popup, and the provider redirects back to the platform's callback.
Providers come in two kinds, and the callback behaves differently for each:
| Kind | Example | After the user consents |
|---|---|---|
event | X / Twitter | The event source and its poll job are created immediately; you get a source_id |
source | Stripe | Only the tokens are stored; you get a token_ref to use in the connect flow |
List providers
The catalog of registered providers and whether each has client credentials configured on this deployment:
curl "$API_BASE/v1/ingestion/oauth/providers" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID"from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.ingestion.get_ingestion_oauth_providers()
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.ingestion.getIngestionOauthProviders();
{
"data": {
"status": "ok",
"providers": {
"twitter": { "id": "twitter", "label": "X / Twitter", "kind": "event", "configured": true },
"stripe": { "id": "stripe", "label": "Stripe", "kind": "source", "configured": true }
}
}
}Start the flow
Request the provider's authorization URL, then open it in a popup. For
event providers you can pass a stream name for the resulting event
source:
curl -X POST "$API_BASE/v1/ingestion/oauth/twitter/start" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{ "stream": "x_mentions" }'from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.ingestion.post_ingestion_oauth_by_provider_start("twitter", json={"stream": "x_mentions"})
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.ingestion.postIngestionOauthByProviderStart("twitter", { json: { stream: "x_mentions" } });
{
"data": {
"status": "ok",
"authorize_url": "https://twitter.com/i/oauth2/authorize?response_type=code&client_id=…&state=…",
"state": "9c2f6c1e40a94b3c…"
}
}Request body
| Field | Meaning |
|---|---|
stream optional | event providers only: the stream name for the resulting event source. |
The state (and a PKCE verifier, where the provider supports it) is held
server-side and validated on the callback — you don't need to store it.
Errors
| Status | Why | Example message |
|---|---|---|
404 | Provider isn't registered | "Unknown OAuth provider: notion" |
400 | Provider registered but no client credentials on this deployment | "Stripe OAuth isn't configured on this deployment. Set oauth.stripe.client_id/client_secret." |
The callback
GET /v1/ingestion/oauth/{provider}/callback is the redirect target the
provider calls — it's public (validated by state), meant for the
browser, and returns an HTML page rather than JSON. The page posts a
message to the window that opened the popup:
// event provider — the source already exists
{ "type": "oauth_done", "provider": "twitter", "source_id": "3f8a1c2e-…" }
// source provider — continue through connect with the token_ref
{ "type": "oauth_done", "provider": "stripe", "token_ref": "tok_9b41…" }
// failure
{ "type": "oauth_done", "provider": "stripe", "error": "access_denied" }Listen for that oauth_done message in your app; don't call the callback
URL yourself.
Using a token_ref
For source providers, feed the token_ref into the normal connect flow
as OAuth credentials:
curl -X POST "$API_BASE/v1/ingestion/connect/inspect" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"connector_type": "stripe",
"credentials": { "auth_type": "oauth", "token_ref": "tok_9b41…" }
}'from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.ingestion.post_ingestion_connect_inspect(json={
"connector_type": "stripe",
"credentials": {"auth_type": "oauth", "token_ref": "tok_9b41…"},
})
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.ingestion.postIngestionConnectInspect({
json: {
connector_type: "stripe",
credentials: { auth_type: "oauth", token_ref: "tok_9b41…" },
},
});
Then continue with connect/create
as usual. A token_ref only works in the workspace that authorized it —
using it elsewhere fails with
"This connected account isn't available in this workspace. Reconnect it."
Tokens are stored encrypted and refreshed automatically; their lifecycle
follows the source they're bound to.

