Supyagent
Connected Accounts

Making API Calls

Route provider API calls to connected accounts using the X-Account-Id header — the core pattern for calling Gmail, Slack, GitHub, and other providers on behalf of your users.

Making API Calls

Once a connected account has an active integration, you can make provider API calls on their behalf. Every provider call uses the same pattern: your API key for authentication, plus the X-Account-Id header to specify which connected account to act as.

The X-Account-Id Header

All provider API calls require the X-Account-Id header to identify which connected account's tokens to use:

GET /api/v1/google/gmail/messages HTTP/1.1
Host: app.supyagent.com
Authorization: Bearer sk_live_your_key_here
X-Account-Id: user-123
DetailValue
Header nameX-Account-Id
ValueThe account's external_id (your internal user identifier)
RequiredYes, for all /api/v1/{provider}/* endpoints

Use the account's external_id (e.g., user-123), not the Supyagent UUID. The external_id is the identifier you provided when creating the account.

How It Works

When Supyagent receives a provider API call:

  1. Validates your API key — checks the Authorization: Bearer header
  2. Resolves the connected account — looks up the X-Account-Id value against your partner's accounts
  3. Checks permissions — verifies the account has the required integration and service enabled
  4. Retrieves tokens — decrypts the stored OAuth tokens for the specified provider
  5. Auto-refreshes if needed — if the access token is expired, uses the refresh token to get a new one
  6. Calls the provider API — forwards the request to the provider (Google, Slack, etc.)
  7. Returns the response — wraps the provider response in the standard Supyagent envelope

Provider Examples

Google Gmail

# List recent messages
curl https://app.supyagent.com/api/v1/google/gmail/messages \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -H "X-Account-Id: user-123"

# Get a specific message
curl https://app.supyagent.com/api/v1/google/gmail/messages/18e4a2b3c4d5e6f7 \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -H "X-Account-Id: user-123"

# Send an email
curl -X POST https://app.supyagent.com/api/v1/google/gmail/messages/send \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -H "X-Account-Id: user-123" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "recipient@example.com",
    "subject": "Hello",
    "body": "Sent via the Connected Accounts API"
  }'
const headers = {
  "Authorization": "Bearer sk_live_your_key_here",
  "X-Account-Id": "user-123",
};

// List recent messages
const messages = await fetch(
  "https://app.supyagent.com/api/v1/google/gmail/messages",
  { headers }
).then(r => r.json());

// Send an email
const sent = await fetch(
  "https://app.supyagent.com/api/v1/google/gmail/messages/send",
  {
    method: "POST",
    headers: { ...headers, "Content-Type": "application/json" },
    body: JSON.stringify({
      to: "recipient@example.com",
      subject: "Hello",
      body: "Sent via the Connected Accounts API",
    }),
  }
).then(r => r.json());
import requests

headers = {
    "Authorization": "Bearer sk_live_your_key_here",
    "X-Account-Id": "user-123",
}

# List recent messages
messages = requests.get(
    "https://app.supyagent.com/api/v1/google/gmail/messages",
    headers=headers,
).json()

# Send an email
sent = requests.post(
    "https://app.supyagent.com/api/v1/google/gmail/messages/send",
    headers={**headers, "Content-Type": "application/json"},
    json={
        "to": "recipient@example.com",
        "subject": "Hello",
        "body": "Sent via the Connected Accounts API",
    },
).json()

Google Calendar

# List upcoming events
curl https://app.supyagent.com/api/v1/google/calendar/events \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -H "X-Account-Id: user-123"

# Create an event
curl -X POST https://app.supyagent.com/api/v1/google/calendar/events \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -H "X-Account-Id: user-123" \
  -H "Content-Type: application/json" \
  -d '{
    "summary": "Team Standup",
    "start": "2026-03-01T09:00:00Z",
    "end": "2026-03-01T09:30:00Z"
  }'
const headers = {
  "Authorization": "Bearer sk_live_your_key_here",
  "X-Account-Id": "user-123",
};

// List upcoming events
const events = await fetch(
  "https://app.supyagent.com/api/v1/google/calendar/events",
  { headers }
).then(r => r.json());

// Create an event
const created = await fetch(
  "https://app.supyagent.com/api/v1/google/calendar/events",
  {
    method: "POST",
    headers: { ...headers, "Content-Type": "application/json" },
    body: JSON.stringify({
      summary: "Team Standup",
      start: "2026-03-01T09:00:00Z",
      end: "2026-03-01T09:30:00Z",
    }),
  }
).then(r => r.json());
headers = {
    "Authorization": "Bearer sk_live_your_key_here",
    "X-Account-Id": "user-123",
}

# List upcoming events
events = requests.get(
    "https://app.supyagent.com/api/v1/google/calendar/events",
    headers=headers,
).json()

# Create an event
created = requests.post(
    "https://app.supyagent.com/api/v1/google/calendar/events",
    headers={**headers, "Content-Type": "application/json"},
    json={
        "summary": "Team Standup",
        "start": "2026-03-01T09:00:00Z",
        "end": "2026-03-01T09:30:00Z",
    },
).json()

Slack

# Send a message
curl -X POST https://app.supyagent.com/api/v1/slack/messages \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -H "X-Account-Id: user-123" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "general",
    "text": "Hello from the API!"
  }'

# List channels
curl https://app.supyagent.com/api/v1/slack/channels \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -H "X-Account-Id: user-123"
const headers = {
  "Authorization": "Bearer sk_live_your_key_here",
  "X-Account-Id": "user-123",
};

// Send a message
const message = await fetch(
  "https://app.supyagent.com/api/v1/slack/messages",
  {
    method: "POST",
    headers: { ...headers, "Content-Type": "application/json" },
    body: JSON.stringify({
      channel: "general",
      text: "Hello from the API!",
    }),
  }
).then(r => r.json());

// List channels
const channels = await fetch(
  "https://app.supyagent.com/api/v1/slack/channels",
  { headers }
).then(r => r.json());
headers = {
    "Authorization": "Bearer sk_live_your_key_here",
    "X-Account-Id": "user-123",
}

# Send a message
message = requests.post(
    "https://app.supyagent.com/api/v1/slack/messages",
    headers={**headers, "Content-Type": "application/json"},
    json={"channel": "general", "text": "Hello from the API!"},
).json()

# List channels
channels = requests.get(
    "https://app.supyagent.com/api/v1/slack/channels",
    headers=headers,
).json()

GitHub

# List repositories
curl https://app.supyagent.com/api/v1/github/repos \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -H "X-Account-Id: user-123"

# List issues for a repository
curl https://app.supyagent.com/api/v1/github/repos/owner/repo/issues \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -H "X-Account-Id: user-123"
const headers = {
  "Authorization": "Bearer sk_live_your_key_here",
  "X-Account-Id": "user-123",
};

// List repositories
const repos = await fetch(
  "https://app.supyagent.com/api/v1/github/repos",
  { headers }
).then(r => r.json());

// List issues
const issues = await fetch(
  "https://app.supyagent.com/api/v1/github/repos/owner/repo/issues",
  { headers }
).then(r => r.json());
headers = {
    "Authorization": "Bearer sk_live_your_key_here",
    "X-Account-Id": "user-123",
}

# List repositories
repos = requests.get(
    "https://app.supyagent.com/api/v1/github/repos",
    headers=headers,
).json()

# List issues
issues = requests.get(
    "https://app.supyagent.com/api/v1/github/repos/owner/repo/issues",
    headers=headers,
).json()

Checking Integration Status

Before making provider calls, verify the account has an active integration:

curl https://app.supyagent.com/api/v1/accounts/550e8400-e29b-41d4-a716-446655440000/integrations \
  -H "Authorization: Bearer sk_live_your_key_here"
const accountId = "550e8400-e29b-41d4-a716-446655440000";

const { data } = await fetch(
  `https://app.supyagent.com/api/v1/accounts/${accountId}/integrations`,
  {
    headers: { "Authorization": "Bearer sk_live_your_key_here" },
  }
).then(r => r.json());

const google = data.integrations.find(
  (i: { provider: string }) => i.provider === "google"
);

if (google?.status === "active") {
  // Safe to make Google API calls
}
account_id = "550e8400-e29b-41d4-a716-446655440000"

response = requests.get(
    f"https://app.supyagent.com/api/v1/accounts/{account_id}/integrations",
    headers={"Authorization": "Bearer sk_live_your_key_here"},
).json()

google = next(
    (i for i in response["data"]["integrations"] if i["provider"] == "google"),
    None,
)

if google and google["status"] == "active":
    # Safe to make Google API calls
    pass

Integration statuses:

StatusMeaning
activeTokens are valid — API calls will succeed
pendingOAuth flow started but not completed
expiredTokens expired and auto-refresh failed — user must re-authorize
revokedUser revoked access from the provider's settings — user must re-authorize

Error Handling

Account Not Found

If the X-Account-Id value doesn't match any account under your partner profile:

{
  "ok": false,
  "error": "Connected account \"invalid-id\" not found"
}

Fix: Verify the external_id matches what you used when creating the account.

No Active Integration

If the account exists but doesn't have an active integration for the requested provider:

{
  "ok": false,
  "error": "No active google integration for this account",
  "error_code": "INTEGRATION_NOT_FOUND"
}

Fix: Initiate a connect session for the missing provider.

Token Errors

If the stored tokens are invalid or the refresh failed:

{
  "ok": false,
  "error": "Failed to refresh access token",
  "error_code": "INTEGRATION_TOKEN_ERROR"
}

Fix: The user needs to re-authorize. Create a new connect session for the provider.

Best Practices

  1. Check integration status first — before making provider calls, verify the integration is active to avoid token errors
  2. Handle token errors gracefully — if you get INTEGRATION_TOKEN_ERROR, prompt the user to re-connect the provider
  3. Use the external_id consistently — map your internal user IDs to Supyagent's external_id at account creation time, then use them for all API calls
  4. Don't cache tokens — Supyagent handles token storage, refresh, and encryption. Always go through the API
  5. Monitor rate limits — check the X-RateLimit-* headers in responses. See Usage & Billing for tier details