API Authentication Guide

Complete guide to authenticating with the Picos Health API using API keys and bearer tokens.

Overview

The Picos Health API uses a two-step authentication process:

  1. Generate an API Key - Create a unique API key in your Account Settings
  2. Exchange for Bearer Token - Use your API key to get a temporary bearer token
  3. Make API Requests - Include the bearer token in your request headers

This approach provides enhanced security by allowing you to:

  • Generate and revoke API keys on demand
  • Use short-lived bearer tokens for individual requests
  • Implement rate limiting and usage tracking
  • Rotate credentials without updating all your applications

📌 Important: Throughout this guide, you'll see {org-name} in API URLs. Replace this placeholder with your organization's assigned subdomain (e.g., eeda, kinexion).


Step 1: Generate an API Key

Via Dashboard

  1. Log in to Picos Health Dashboard
  2. Navigate to Account Settings → API Integration
  3. Click Generate API Key
  4. Copy your API key immediately (it won't be shown again)
  5. Store it securely (never commit to version control)

API Key Format

pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • pk_live_ prefix indicates a production key
  • The key is 32 characters of random alphanumeric characters
  • Treat it like a password

Step 2: Exchange API Key for Bearer Token

HTTP Request

POST https://{org-name}.api.picoshealth.com/v1/auth/token
Content-Type: application/json

{
  "apiKey": "YOUR_API_KEY"
}

Response

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresIn": 3600,
  "tokenType": "Bearer"
}

Response Fields:

  • token - The bearer token to use in API requests
  • expiresIn - Token expiration time in seconds (default: 1 hour)
  • tokenType - Always "Bearer"

cURL Example

curl -X POST https://{org-name}.api.picoshealth.com/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{
    "apiKey": "pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }'

JavaScript Example

async function getAuthToken(apiKey) {
  const response = await fetch('https://{org-name}.api.picoshealth.com/v1/auth/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      apiKey: apiKey,
    }),
  });

  if (!response.ok) {
    throw new Error(`Auth failed: ${response.statusText}`);
  }

  return response.json();
}

// Usage
const { token, expiresIn } = await getAuthToken('pk_live_...');
console.log(`Token valid for ${expiresIn} seconds`);

Python Example

import requests
import json

def get_auth_token(api_key):
    url = "https://{org-name}.api.picoshealth.com/v1/auth/token"
    headers = {
        "Content-Type": "application/json"
    }
    payload = {
        "apiKey": api_key
    }
    
    response = requests.post(url, headers=headers, json=payload)
    response.raise_for_status()
    
    return response.json()

# Usage
result = get_auth_token("pk_live_...")
token = result['token']
expires_in = result['expiresIn']
print(f"Token valid for {expires_in} seconds")

Step 3: Make API Requests

Using Bearer Token

Include the token in the Authorization header:

GET https://{org-name}.api.picoshealth.com/v1/users
Authorization: Bearer YOUR_BEARER_TOKEN

JavaScript Fetch Example

async function fetchUsers(token) {
  const response = await fetch('https://{org-name}.api.picoshealth.com/v1/users?limit=10', {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
  });

  if (!response.ok) {
    throw new Error(`API request failed: ${response.statusText}`);
  }

  return response.json();
}

// Usage
const users = await fetchUsers(token);
console.log(users.docs);

Axios Example

import axios from 'axios';

const api = axios.create({
  baseURL: 'https://{org-name}.api.picoshealth.com/v1',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
});

// Make requests with token automatically included
const response = await api.get('/users', { params: { limit: 10 } });
console.log(response.data.docs);

Python Requests Example

import requests

def make_api_request(token, endpoint, method='GET', params=None, data=None):
    url = f"https://{{org-name}}.api.picoshealth.com/v1{endpoint}"
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }
    
    if method == 'GET':
        response = requests.get(url, headers=headers, params=params)
    elif method == 'POST':
        response = requests.post(url, headers=headers, json=data)
    elif method == 'PATCH':
        response = requests.patch(url, headers=headers, json=data)
    elif method == 'DELETE':
        response = requests.delete(url, headers=headers)
    
    response.raise_for_status()
    return response.json()

# Usage
users = make_api_request(token, '/users', params={'limit': 10})
print(users['docs'])

Complete Workflow Example

JavaScript

// 1. Get API key from user (stored securely)
const apiKey = process.env.PICOS_API_KEY;

// 2. Exchange for bearer token
async function authenticateAndFetch() {
  try {
    // Get token
    const authResponse = await fetch('https://{org-name}.api.picoshealth.com/v1/auth/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ apiKey }),
    });

    if (!authResponse.ok) {
      throw new Error('Authentication failed');
    }

    const { token, expiresIn } = await authResponse.json();
    console.log(`✓ Authenticated, token valid for ${expiresIn}s`);

    // 3. Use token to make API requests
    const usersResponse = await fetch('https://{org-name}.api.picoshealth.com/v1/users', {
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
    });

    const users = await usersResponse.json();
    console.log(`✓ Retrieved ${users.totalDocs} users`);
    console.log('First user:', users.docs[0]);

    // Create a new product
    const productResponse = await fetch('https://{org-name}.api.picoshealth.com/v1/products', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        title: 'Compression Stockings',
        price: 49.99,
        currency: 'USD',
        status: 'draft',
      }),
    });

    const product = await productResponse.json();
    console.log('✓ Created product:', product.id);

  } catch (error) {
    console.error('Error:', error.message);
  }
}

authenticateAndFetch();

Python

import os
import requests
from datetime import datetime, timedelta

class PicosAPIClient:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://{org-name}.api.picoshealth.com/v1"
        self.token = None
        self.token_expires = None

    def authenticate(self):
        """Exchange API key for bearer token"""
        url = f"{self.base_url}/auth/token"
        response = requests.post(url, json={"apiKey": self.api_key})
        response.raise_for_status()

        data = response.json()
        self.token = data['token']
        self.token_expires = datetime.now() + timedelta(seconds=data['expiresIn'])
        print(f"✓ Authenticated, token valid until {self.token_expires}")

    def _request(self, method, endpoint, **kwargs):
        """Make authenticated API request"""
        if not self.token or datetime.now() >= self.token_expires:
            self.authenticate()

        url = f"{self.base_url}{endpoint}"
        headers = {
            "Authorization": f"Bearer {self.token}",
            "Content-Type": "application/json",
        }

        response = requests.request(method, url, headers=headers, **kwargs)
        response.raise_for_status()
        return response.json()

    def get_users(self, limit=10):
        """Get users"""
        return self._request("GET", "/users", params={"limit": limit})

    def create_product(self, title, price, currency="USD"):
        """Create a product"""
        return self._request(
            "POST",
            "/products",
            json={
                "title": title,
                "price": price,
                "currency": currency,
                "status": "draft",
            },
        )

    def get_orders(self, limit=20):
        """Get orders"""
        return self._request("GET", "/orders", params={"limit": limit})


# Usage
if __name__ == "__main__":
    api_key = os.getenv("PICOS_API_KEY")
    client = PicosAPIClient(api_key)

    # Get users
    users_data = client.get_users(limit=5)
    print(f"✓ Retrieved {users_data['totalDocs']} users")
    print("First user:", users_data['docs'][0])

    # Create product
    product = client.create_product(
        title="Compression Stockings",
        price=49.99,
    )
    print("✓ Created product:", product['id'])

    # Get orders
    orders_data = client.get_orders(limit=10)
    print(f"✓ Retrieved {orders_data['totalDocs']} orders")

Error Handling

Authentication Errors

ErrorCauseSolution
401 UnauthorizedInvalid or missing API keyVerify API key in Account Settings
401 UnauthorizedExpired bearer tokenRequest a new token with your API key
403 ForbiddenInsufficient permissionsVerify your user role has API access
429 Too Many RequestsRate limit exceededImplement exponential backoff and retry logic

Common Responses

Invalid API Key:

{
  "error": "Invalid API key",
  "code": "INVALID_API_KEY",
  "status": 401
}

Expired Token:

{
  "error": "Token expired",
  "code": "TOKEN_EXPIRED",
  "status": 401
}

Permission Denied:

{
  "error": "Insufficient permissions",
  "code": "FORBIDDEN",
  "status": 403
}

Security Best Practices

✅ Do

  • Store API keys securely - Use environment variables, secrets managers, or vault systems
  • Rotate API keys regularly - Generate new keys monthly or when team members leave
  • Use HTTPS only - All requests must use HTTPS
  • Keep tokens short-lived - Use the default 1-hour expiration
  • Implement rate limiting - Handle 429 responses gracefully
  • Log authentication failures - Monitor for suspicious activity

❌ Don't

  • Commit API keys to version control - Use .gitignore and environment files
  • Expose API keys in client-side code - Keep keys server-side only
  • Share API keys in emails or chat - Use secure credential management
  • Use the same key for multiple environments - Generate separate keys per environment
  • Leave tokens in browser storage - Keep them in secure, httpOnly cookies
  • Log full tokens in production - Only log masked versions

Rate Limiting

All API endpoints have rate limits:

  • Standard tier: 100 requests/minute per API key
  • Professional tier: 1,000 requests/minute per API key
  • Enterprise tier: Custom limits based on agreement

Rate Limit Headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1640000000

When Rate Limited (429):

{
  "error": "Too many requests",
  "retryAfter": 60,
  "code": "RATE_LIMITED"
}

Implement exponential backoff retry logic:

async function requestWithRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.status === 429 && i < maxRetries - 1) {
        const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
}

Endpoint Reference

All authenticated endpoints follow this pattern:

{METHOD} https://{org-name}.api.picoshealth.com/v1/{endpoint}
Authorization: Bearer {token}
Content-Type: application/json

Collections

Core Entities

  • GET /users - List users
  • GET /users/{id} - Get user
  • POST /users - Create user
  • PATCH /users/{id} - Update user
  • DELETE /users/{id} - Delete user

Products

  • GET /products - List products
  • POST /products - Create product
  • PATCH /products/{id} - Update product

Orders

  • GET /orders - List orders
  • POST /orders - Create order
  • PATCH /orders/{id} - Update order status

Cart (new, v2 only - https://{org-name}.api.picoshealth.com/v2/cart)

  • GET /cart - View the current user's cart
  • POST /cart - Add a product variant to the cart
  • PATCH /cart/{id} - Update a cart item's quantity
  • DELETE /cart/{id} - Remove a cart item

And many more...

Note: Your bearer token identifies exactly one user (the sub claim, resolved from your API key). Cart and order-checkout endpoints always act on that user - there is no userId parameter to set. See Placing an Order Through the API for the full cart → checkout flow.

Globals

  • GET /globals/site-settings - Get site settings
  • POST /globals/site-settings - Update site settings
  • GET /globals/organization-settings - Get org settings
  • GET /globals/wallet - Get wallet config

See Full API Documentation for complete endpoint reference.


Support

For questions or issues: