API Documentation

Programmatic access to your StaffPhotoBoard data

Contents

Authentication

The StaffPhotoBoard API uses API keys for authentication. You can create and manage API keys from your Organisation Settings page.

Using Your API Key

Include your API key in requests using one of these methods:

Header (Recommended)

X-API-Key: spb_live_abc123...

Query Parameter

?api_key=spb_live_abc123...

Security Note: Keep your API keys secure and never share them publicly. Revoke any keys that may have been exposed.

API Endpoints

Replace Board Members

PUT /api/v1/boards/:boardId/replace

Replace all members on a board with new data. Useful for bulk imports from HRIS systems.

Request Body

{
  "members": [
    {
      "name": "John Doe",
      "title": "CEO",
      "email": "[email protected]",
      "bio": "Leading the company since 2020",
      "department": "Executive",
      "order": 0,
      "photo": "https://example.com/photo.jpg"  // Optional: URL or base64
    }
  ],
  "metadata": {
    "source": "HRIS Integration",
    "syncedAt": "2026-01-06T00:00:00Z"
  }
}

Photo Upload Options

The photo field is optional and supports two formats:

1. Photo URL (Recommended)

Provide a URL to the photo. The API will fetch and upload it automatically.

"photo": "https://hris.example.com/api/employees/12345/photo.jpg"

Best for: HRIS integrations, larger images, photos already hosted elsewhere

2. Base64 Encoded Image

Encode the image as a base64 data URI and include it directly in the request.

"photo": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD..."

Best for: Small images (<1MB), direct uploads, when you have the image data

3. No Photo

Simply omit the photo field if you don't want to upload a photo.

{
  "name": "John Doe",
  "title": "CEO",
  "email": "[email protected]",
  "order": 0
}

Supported formats: JPEG, PNG, GIF and other standard image formats. The format is automatically detected from the data URI mime type or the URL response content type.

📐 Recommended Image Specifications

  • Recommended Size: 280×336 pixels (portrait orientation)
  • Aspect Ratio: 5:6 (width:height)
  • File Size: Under 5MB (under 1MB recommended for base64 encoding)
  • Formats: JPEG, PNG, GIF, WebP

Note: Images are automatically processed and optimised for display on photo boards. For best results, use portrait-oriented images close to the recommended 280×336 size.

Response (200 OK)

{
  "boardId": "board_123",
  "membersReplaced": 10,
  "photosUploaded": 8,
  "processedAt": "2026-01-06T00:00:00Z",
  "warnings": [
    {
      "memberId": "member_456",
      "code": "PHOTO_DOWNLOAD_FAILED",
      "message": "Photo URL returned 404"
    }
  ]
}

Rate Limits

API requests are rate-limited to ensure service stability:

  • 100 requests per minute per API key
  • Rate limit resets every 60 seconds
  • Exceeding limits returns 429 Too Many Requests

Rate Limit Headers

RateLimit-Limit: 100
RateLimit-Remaining: 95
RateLimit-Reset: 1704585600

Error Handling

Errors follow RFC 7807 Problem Details format:

{
  "type": "https://api.staffphotoboard.paneone.net/errors/rate-limit-exceeded",
  "title": "Rate Limit Exceeded",
  "status": 429,
  "detail": "You have exceeded the rate limit of 100 requests per 60 seconds.",
  "instance": "/api/v1/boards/board_123/replace",
  "retryAfter": 60
}

Common Error Codes

Code Description
400 Bad Request - Invalid input
401 Unauthorised - Invalid API key
403 Forbidden - Insufficient permissions
404 Not Found - Resource doesn't exist
429 Too Many Requests - Rate limit exceeded
500 Internal Server Error - Service error

Example Requests

cURL (with Photo URL)

curl -X PUT https://api.staffphotoboard.paneone.net/api/v1/boards/board_123/replace \
  -H "X-API-Key: spb_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "members": [
      {
        "name": "Jane Smith",
        "title": "Engineering Manager",
        "email": "[email protected]",
        "order": 0,
        "photo": "https://hris.example.com/photos/jane-smith.jpg"
      }
    ]
  }'

cURL (with Base64 Photo)

# Convert image to base64
BASE64_IMAGE=$(base64 -i photo.jpg | tr -d '\n')

# Make the request
curl -X PUT https://api.staffphotoboard.paneone.net/api/v1/boards/board_123/replace \
  -H "X-API-Key: spb_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "members": [
      {
        "name": "Jane Smith",
        "title": "Engineering Manager",
        "email": "[email protected]",
        "order": 0,
        "photo": "data:image/jpeg;base64,'$BASE64_IMAGE'"
      }
    ]
  }'

JavaScript (Node.js with Photo URL)

const response = await fetch(
  'https://api.staffphotoboard.paneone.net/api/v1/boards/board_123/replace',
  {
    method: 'PUT',
    headers: {
      'X-API-Key': 'spb_live_abc123...',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      members: [
        {
          name: 'Jane Smith',
          title: 'Engineering Manager',
          email: '[email protected]',
          order: 0,
          photo: 'https://hris.example.com/photos/jane-smith.jpg'
        }
      ]
    })
  }
);

const data = await response.json();
console.log(data);

JavaScript (Node.js with Base64 Photo)

const fs = require('fs');

// Read and encode image
const imageBuffer = fs.readFileSync('photo.jpg');
const base64Image = `data:image/jpeg;base64,${imageBuffer.toString('base64')}`;

const response = await fetch(
  'https://api.staffphotoboard.paneone.net/api/v1/boards/board_123/replace',
  {
    method: 'PUT',
    headers: {
      'X-API-Key': 'spb_live_abc123...',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      members: [
        {
          name: 'Jane Smith',
          title: 'Engineering Manager',
          email: '[email protected]',
          order: 0,
          photo: base64Image
        }
      ]
    })
  }
);

const data = await response.json();
console.log(data);

Python (with Photo URL)

import requests

url = "https://api.staffphotoboard.paneone.net/api/v1/boards/board_123/replace"
headers = {
    "X-API-Key": "spb_live_abc123...",
    "Content-Type": "application/json"
}
data = {
    "members": [
        {
            "name": "Jane Smith",
            "title": "Engineering Manager",
            "email": "[email protected]",
            "order": 0,
            "photo": "https://hris.example.com/photos/jane-smith.jpg"
        }
    ]
}

response = requests.put(url, headers=headers, json=data)
print(response.json())

Python (with Base64 Photo)

import requests
import base64

# Read and encode image
with open('photo.jpg', 'rb') as image_file:
    base64_image = base64.b64encode(image_file.read()).decode('utf-8')
    photo_data_uri = f"data:image/jpeg;base64,{base64_image}"

url = "https://api.staffphotoboard.paneone.net/api/v1/boards/board_123/replace"
headers = {
    "X-API-Key": "spb_live_abc123...",
    "Content-Type": "application/json"
}
data = {
    "members": [
        {
            "name": "Jane Smith",
            "title": "Engineering Manager",
            "email": "[email protected]",
            "order": 0,
            "photo": photo_data_uri
        }
    ]
}

response = requests.put(url, headers=headers, json=data)
print(response.json())

Need Help?

Have questions about the API? We're here to help.

Contact Support