Skip to main content

Authentication

The SalesOS Integration API uses API Keys for authentication. Each key is scoped to a single tenant, hashed with bcrypt, and supports rate limiting and IP allowlists.

Environments

Base URL: https://api.play2sell.comDashboard: https://dashboard.play2sell.comApp: https://app.play2sell.com

Quick Start

1. Create an API Key

Go to Integrations > API Keys in the SalesOS Dashboard:
  1. Click Create API Key
  2. Name your key (e.g., “CRM Nightly Sync”, “Website Form Integration”)
  3. Select the scope: default:sync
  4. Click Create
  5. Copy the key immediately — it will only be shown once
Your key looks like:
sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

2. Use the Key in Requests

Include the key in the Authorization header of every API request:
curl -X POST https://api.play2sell.com/functions/v1/default-integration \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "sync_collaborators",
    "collaborators": [
      {
        "external_id": "emp-101",
        "name": "Maria Santos",
        "email": "maria@yourcompany.com"
      }
    ]
  }'

3. Check the Response

Success (200):
{
  "data": { "created": 1, "existing": 0, "errors": [], "total": 1 },
  "meta": { "request_id": "f47ac10b-...", "timestamp": "2026-03-17T10:30:00.000Z" }
}
Invalid key (401):
{
  "error": { "code": "UNAUTHORIZED", "message": "Invalid or expired API key" }
}

API Key Properties

PropertyDetails
Prefixsk_live_ (production) or sk_test_ (testing)
Scopedefault:sync — enables sync_collaborators and sync_activities
Rate limitConfigurable per key (default: 1000 requests/hour)
ExpirationOptional — set an expiry date or leave as never-expires
IP allowlistOptional — restrict to specific IP addresses
StorageHashed with bcrypt — the plaintext key is never stored

Key Formats

SalesOS uses two key prefixes to distinguish environments:
PrefixEnvironmentUse case
sk_live_ProductionReal data, real missions, real points
sk_test_TestingSafe to use during development — no impact on production
Use sk_test_ keys during development and integration testing. Switch to sk_live_ when you go to production.

Authentication Errors

HTTP StatusError CodeMeaningWhat to do
401UNAUTHORIZEDKey is missing, invalid, or expiredCheck the Authorization header. Verify the key in the Dashboard.
403FORBIDDENKey is valid but lacks required scopeEdit the key and add the default:sync scope
429RATE_LIMITEDToo many requests this hourWait retry_after seconds, then retry

Example: Missing Authorization header

curl -X POST https://api.play2sell.com/functions/v1/default-integration \
  -H "Content-Type: application/json" \
  -d '{"action": "sync_collaborators", "collaborators": [...]}'
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Missing or malformed Authorization header. Expected: Bearer sk_live_xxx"
  }
}

Example: Wrong key prefix

# Using "Bearer mytoken123" instead of "Bearer sk_live_..."
curl -X POST https://api.play2sell.com/functions/v1/default-integration \
  -H "Authorization: Bearer mytoken123456789" \
  -H "Content-Type: application/json" \
  -d '{"action": "sync_collaborators", "collaborators": [...]}'
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid API key prefix. Expected sk_live_ or sk_test_"
  }
}

Example: Key without required scope

If your key only has leads:read but the endpoint requires default:sync:
{
  "error": {
    "code": "FORBIDDEN",
    "message": "API key missing required scopes: default:sync"
  }
}

Example: Rate limit exceeded

{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded",
    "retry_after": 1847
  }
}
The retry_after field tells you how many seconds to wait. The rate limit window resets every hour.

Rate Limits

Each API key has an independent rate limit counter that resets hourly:
SettingDefaultRange
Requests per hour10001 — 100,000
How it works:
  1. Each successful request increments the counter
  2. When the counter reaches the limit, further requests return 429
  3. The counter resets to 0 one hour after the first request in the window
Handling rate limits in code:
async function callWithRetry(payload) {
  const response = await fetch(API_URL, {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });

  if (response.status === 429) {
    const { error } = await response.json();
    const waitSeconds = error.retry_after || 60;
    console.log(`Rate limited. Retrying in ${waitSeconds}s...`);
    await new Promise(r => setTimeout(r, waitSeconds * 1000));
    return callWithRetry(payload); // retry
  }

  return response.json();
}

Security Best Practices

Never expose API keys in client-side code. Browser JavaScript, mobile apps, and public repositories can all leak your key. Always call the SalesOS API from your backend server.
  • Use environment variables — Store SALESOS_API_KEY in env vars or a secrets manager, never in source code
  • Rotate keys periodically — Create a new key, update your integration, then revoke the old one
  • Use IP allowlists — If your integration runs from fixed IPs, restrict the key to those IPs only
  • Monitor usage — Check the API usage logs in the Dashboard for unexpected patterns
  • Use sk_test_ for development — Test keys isolate your dev environment from production
  • Revoke compromised keys immediately — Go to Dashboard > Admin > API Keys > Revoke

Key rotation example

# 1. Create new key in Dashboard → copy sk_live_NEW_KEY
# 2. Update your env var
export SALESOS_API_KEY=sk_live_NEW_KEY

# 3. Verify it works
curl -s -X POST "$API_URL" \
  -H "Authorization: Bearer $SALESOS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action":"sync_collaborators","collaborators":[{"external_id":"test","name":"Test","email":"test@co.com"}]}' | jq .

# 4. Revoke old key in Dashboard

Self-Service API Key Management

In addition to creating keys in the Dashboard, tenant admins can manage API keys programmatically via the API. This enables:
  • Automated key rotation in CI/CD pipelines
  • Partner onboarding workflows that provision keys on demand
  • Custom admin panels that integrate key management

API Key Management

Create, list, and revoke keys via API using your Dashboard JWT token

Next Steps

Default Integration

Start sending activities to SalesOS

API Keys

Manage keys programmatically