API Reference

Authentication

Learn how to authenticate with OAuth 2.0 access tokens

Log in to see your API keys
API KeyLabelLast Used

Authentication Guide

Simple,secure B2B authentication for the Speculo API.

Overview

The Speculo API uses long-lived OAuth 2.0 access tokens for server-to-server integrations. No complex refresh logic, no expiry tracking—just get a token once and use it.

📚

Need to understand permissions? Check out OAuth Scopes.

Who does what?

  • Speculo support provisions your OAuth credentials (clientId, refreshToken, orgId)
  • You exchange the refresh token for an access token and use it on all API requests

Step 1: Collect your credentials

You'll receive these from the Speculo team (or provision them via the internal admin API):

NameDescriptionExample
clientIdIdentifies your OAuth clientclient_abc123
refreshTokenLong-lived secret for minting access tokensrt_live_xxxxxxxxx
orgIdYour organization identifierorg_abc123
scopesPermissions granted to the client["contacts.read","contacts.write"]

Security: Treat the refresh token like a password. Store it securely (environment variables, secret manager, etc.).

Step 2: Exchange refresh token for an access token

Call this once when you first set up your integration:

curl -X POST https://speculo-api-oauth-54875993561.us-central1.run.app/token \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "client_abc123",
    "refreshToken": "rt_live_xxxxxxxxx"
  }'

Example response:

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "tokenType": "Bearer",
  "scope": "contacts.read contacts.write",
  "scopes": ["contacts.read", "contacts.write"]
}

Important: This access token does not expire. Save it and reuse it for all API requests.

Step 3: Call the APIs

Use the access token on every request. Services can optionally enforce scopes by decoding the JWT or calling /introspect.

curl -X GET "https://api.speculo.ai/v1/contacts?pageSize=50" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Example with Python:

import requests

ACCESS_TOKEN = "eyJhbGciOiJIUzI1NiIs..."

headers = {
    "Authorization": f"Bearer {ACCESS_TOKEN}"
}

response = requests.get(
    "https://speculo-api-contacts-uc.a.run.app/v1/contacts",
    headers=headers,
    params={"pageSize": 50}
)

contacts = response.json()

Example with Node.js:

const axios = require('axios');

const ACCESS_TOKEN = 'eyJhbGciOiJIUzI1NiIs...';

const response = await axios.get(
  'https://api.speculo.ai/v1/contacts',
  {
    headers: {
      'Authorization': `Bearer ${ACCESS_TOKEN}`
    },
    params: {
      pageSize: 50
    }
  }
);

const contacts = response.data;

Token lifecycle

Long-lived tokens (default)

Access tokens never expire by default. This simplifies B2B integrations:

  • ✅ No refresh logic needed
  • ✅ No token expiry tracking
  • ✅ Reliable integrations that don't break

When to get a new token

Only call /token again when:

  1. Rotating credentials - Periodic security rotation (recommended every 6-12 months)
  2. Revoking access - When you need to invalidate the old token
  3. Team member leaves - When someone with access to credentials leaves

How to rotate credentials

  1. Call /token with your existing refresh token to get a new access token
  2. Update your application to use the new access token
  3. (Optional) Contact Speculo support to revoke the old refresh token

Security best practices

Do:

  • ✅ Store refresh tokens in environment variables or secret managers
  • ✅ Use HTTPS for all API calls (enforced by our services)
  • ✅ Rotate credentials periodically (every 6-12 months)
  • ✅ Request only the scopes you need (principle of least privilege)
  • ✅ Revoke credentials when team members with access leave

Don't:

  • ❌ Hard-code refresh tokens in your source code
  • ❌ Commit credentials to version control
  • ❌ Share refresh tokens via email or messaging apps
  • ❌ Reuse the same credentials across multiple environments

Troubleshooting

401 Unauthorized on /token

Possible causes:

  • Incorrect clientId or refreshToken
  • Whitespace/newlines when copying credentials
  • Refresh token has been revoked

Solution: Verify credentials and check for copy/paste errors. Contact Speculo support if the issue persists.

401 Unauthorized on API calls

Possible causes:

  • Missing or malformed Authorization header
  • Access token was not properly obtained from /token
  • Refresh token was revoked (and access token is no longer valid)

Solution: Verify the Authorization: Bearer {token} header format and ensure you're using the access token from the /token response.

403 Forbidden

Possible causes:

  • Client status is not "active" (may be revoked)
  • Organization permissions issue
  • Access token missing a required scope

Solution: Contact Speculo support to verify client status/scopes, or request new credentials with the appropriate scopes.

Need help?

Contact [email protected] with:

  • Your organization ID
  • Your client ID
  • Timestamp of the failing request (UTC)
  • Request/response details (never include refresh tokens or access tokens in email)

We're happy to help with authentication, troubleshooting, or credential rotation.

Credentials
Click Try It! to start a request and see the response here!