Authentication

API Keys

Create and manage API keys for server-to-server authentication

API keys enable server-to-server communication with the Synoveo API. They are used by the WordPress plugin, custom integrations, and background services.

Overview

An API key consists of two parts:

ComponentFormatExample
Client IDsyncid_{user_id}_{timestamp}_{name}syncid_570_1703030400000_wordpress
Client Secret48 bytes, base64-encodeda3J5cHRvZ3JhcGhp...

Security: The client secret is shown only once during creation. Store it securely in environment variables.


Creating API Keys

Via Dashboard

  1. Navigate to Dashboard → Developer
  2. Click Create API Key
  3. Configure the key:
    • Name: Descriptive identifier (e.g., "Production WordPress")
    • Location: Select the Google Business location to connect
    • Domain: (Optional) Restrict to specific domain
  4. Click Create
  5. Copy and securely store the credentials

Via API

POST /api/v1/developer/credentials HTTP/1.1
Host: api.synoveo.com
Authorization: Bearer <user_jwt_token>
Content-Type: application/json

{
  "name": "My WordPress Site",
  "business_id": 123,
  "assigned_location_id": "locations/456789",
  "primary_domain": "example.com",
  "allowed_domains": ["staging.example.com"]
}

Request Body:

FieldTypeRequiredDescription
namestringYesDescriptive name for the API key
business_idnumberYesID of the business
assigned_location_idstringYesGoogle location resource name
primary_domainstringNoPrimary domain for CORS validation
allowed_domainsarrayNoAdditional allowed domains

Response:

{
  "status": "ok",
  "data": {
    "client_id": "syncid_570_1703030400000_my_wordpress_site",
    "client_secret": "a3J5cHRvZ3JhcGhpY2FsbHlfc2VjdXJlX3JhbmRvbV9ieXRlcw==",
    "service_client_id": 5432,
    "name": "My WordPress Site",
    "plan": "pro",
    "business_id": 123,
    "business_name": "My Business",
    "assigned_location_id": "locations/456789",
    "primary_domain": "example.com",
    "allowed_domains": ["example.com", "www.example.com", "staging.example.com"],
    "created_at": "2025-01-15T10:30:00Z",
    "warning": "Save this secret securely. It will not be shown again."
  }
}

Using API Keys

Step 1: Exchange for JWT Token

Use the OAuth2 client credentials flow to exchange your API key for a JWT token:

curl -X POST https://api.synoveo.com/api/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "syncid_570_1703030400000_wordpress",
    "client_secret": "your_base64_secret"
  }'

Response:

{
  "status": "ok",
  "data": {
    "token_type": "Bearer",
    "scope": "service",
    "plan": "pro",
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expires_in": 7776000,
    "permissions": ["business.read", "business.write"],
    "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}

Step 2: Use the Token

Include the JWT token in API requests:

curl https://api.synoveo.com/api/v1/google-business/locations \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Token Caching

Important: Cache tokens and reuse them until they expire. Don't request a new token for every API call.

TokenTTLRecommended Refresh
Access Token90 days5 minutes before expiration
Refresh Token30 daysWhen access token expires

Example Token Cache (Node.js)

let cachedToken = null
let tokenExpiresAt = null

async function getToken() {
  // Return cached token if still valid (with 5 min buffer)
  if (cachedToken && tokenExpiresAt > Date.now() + 300000) {
    return cachedToken
  }

  const response = await fetch('https://api.synoveo.com/api/v1/auth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      grant_type: 'client_credentials',
      client_id: process.env.SYNOVEO_CLIENT_ID,
      client_secret: process.env.SYNOVEO_CLIENT_SECRET
    })
  })

  const { data } = await response.json()
  cachedToken = data.access_token
  tokenExpiresAt = Date.now() + (data.expires_in * 1000)

  return cachedToken
}

Managing API Keys

List API Keys

GET /api/v1/developer/credentials HTTP/1.1
Host: api.synoveo.com
Authorization: Bearer <user_jwt_token>

Response:

{
  "status": "ok",
  "data": {
    "credentials": [
      {
        "id": 5432,
        "client_id": "syncid_570_1703030400000_wordpress",
        "name": "My WordPress Site",
        "assigned_location_id": "locations/456789",
        "primary_domain": "example.com",
        "created_at": "2025-01-15T10:30:00Z",
        "last_used_at": "2025-01-20T14:22:00Z"
      }
    ]
  }
}

Check Connection Status

GET /api/v1/api-keys/{client_id}/status HTTP/1.1
Host: api.synoveo.com
Authorization: Bearer <user_jwt_token>

Response:

{
  "status": "ok",
  "data": {
    "connection_status": "online",
    "last_heartbeat_at": "2025-01-20T14:22:00Z",
    "plugin_version": "1.2.0",
    "wp_version": "6.4.2",
    "php_version": "8.2.0"
  }
}
StatusDescription
onlineHeartbeat within last 2 hours
idleHeartbeat within last 24 hours
offlineNo heartbeat for 24+ hours

Revoke API Key

DELETE /api/v1/developer/credentials/{client_id} HTTP/1.1
Host: api.synoveo.com
Authorization: Bearer <user_jwt_token>

Warning: Revoking an API key immediately invalidates all tokens issued with it.


WordPress Plugin Integration

The Synoveo WordPress plugin handles API key authentication automatically:

  1. Enter client_id and client_secret in plugin settings
  2. Plugin exchanges credentials for JWT token
  3. Token is cached in WordPress options
  4. Auto-refresh before expiration
  5. Heartbeat sent every hour to monitor connection

Heartbeat Endpoint

The plugin sends periodic heartbeats:

POST /api/v1/api-keys/heartbeat HTTP/1.1
Host: api.synoveo.com
Authorization: Bearer <service_jwt_token>
Content-Type: application/json

{
  "plugin_version": "1.2.0",
  "wp_version": "6.4.2",
  "php_version": "8.2.0",
  "detected_plugins": ["yoast-seo", "woocommerce"]
}

Response:

{
  "status": "ok",
  "data": {
    "received": true,
    "next_heartbeat_in": 3600
  }
}

Validate Credentials

Validate API credentials without issuing a token (useful for WordPress setup):

POST /api/v1/auth/validate-service-credentials HTTP/1.1
Host: api.synoveo.com
Content-Type: application/json

{
  "client_id": "syncid_570_1703030400000_wordpress",
  "client_secret": "your_base64_secret",
  "site_domain": "example.com"
}

Response:

{
  "status": "ok",
  "data": {
    "valid": true,
    "user": {
      "id": 570,
      "email": "user@example.com",
      "name": "John Doe"
    },
    "business": {
      "id": 123,
      "name": "My Business"
    },
    "location": {
      "id": "locations/456789",
      "name": "Downtown Location",
      "address": "123 Main St, City, State 12345",
      "is_verified": true
    },
    "plan": "pro",
    "plan_display_name": "Professional",
    "capabilities": {
      "sync": true,
      "posts": true,
      "reviews": true
    }
  }
}

Deactivated API Keys

API keys can be deactivated without deletion:

{
  "status": "error",
  "error": {
    "code": "AUTH_INSUFFICIENT_PERMISSIONS",
    "message": "API key has been deactivated",
    "details": {
      "deactivation_reason": "billing_issue",
      "deactivated_at": "2025-01-15T10:00:00Z",
      "upgrade_url": "https://app.synoveo.com/dashboard/billing"
    }
  }
}
ReasonDescription
billing_issuePayment failed or subscription cancelled
plan_downgradeLocation limit exceeded after downgrade
security_concernFlagged for suspicious activity
user_requestedManually deactivated by user

Security Best Practices

  1. Environment Variables - Never hardcode secrets in source code
  2. Domain Binding - Restrict keys to specific domains
  3. Location Scoping - Each key accesses only one location
  4. Regular Rotation - Rotate keys periodically
  5. Monitor Usage - Check last_used_at for suspicious activity
  6. Revoke Unused - Delete keys no longer in use

SDK Usage

The @synoveo/sdk handles API key authentication automatically:

import { SynoveoClient } from '@synoveo/sdk'

const client = new SynoveoClient({
  clientId: process.env.SYNOVEO_CLIENT_ID,
  clientSecret: process.env.SYNOVEO_CLIENT_SECRET,
})

// Token management is automatic
const locations = await client.locations.list()

See SDK Documentation for more details.

On this page