Developer Documentation

Integrate 360° feedback into your workflow with our REST API and webhooks

Quick Start API Reference Interactive Docs

API Overview

Everything you need to integrate Blik into your systems

Authentication

Bearer token authentication with organization-scoped access. Create API tokens in your dashboard.

REST API

Full CRUD operations for cycles, reviewees, questionnaires, and reports. Standard HTTP methods with JSON responses.

Webhooks

Real-time event notifications with HMAC signatures. Subscribe to cycle.created, cycle.completed, feedback.submitted, and report.generated events.

OpenAPI Docs

Interactive Swagger documentation at /api/v1/docs/. Test endpoints directly in your browser.

No Limits

No rate limits, no usage caps, no hidden fees. Unlimited API calls included with your subscription.

Secure

Modern webhook structure with non-enumerable UUIDs. HMAC-SHA256 signatures for webhook verification.

Quick Start

Get started in 5 minutes

1. Create an API Token

Navigate to Settings → API Tokens in your dashboard and create a new token with the required permissions.

Name: "My Integration"
Expires: Optional (recommended: 90 days)

Save the token value immediately—it's only shown once.

2. Make Your First Request

Test your authentication by listing reviewees:

curl -X GET 'https://www.blik360.com/api/v1/reviewees/' \
  -H 'Authorization: Bearer YOUR_TOKEN_HERE' \
  -H 'Accept: application/json'
import requests

API_TOKEN = "YOUR_TOKEN_HERE"
BASE_URL = "https://www.blik360.com/api/v1"

headers = {
    "Authorization": f"Bearer {API_TOKEN}",
    "Accept": "application/json"
}

response = requests.get(f"{BASE_URL}/reviewees/", headers=headers)
reviewees = response.json()
print(reviewees)
const API_TOKEN = 'YOUR_TOKEN_HERE';
const BASE_URL = 'https://www.blik360.com/api/v1';

const headers = {
  'Authorization': `Bearer ${API_TOKEN}`,
  'Accept': 'application/json'
};

fetch(`${BASE_URL}/reviewees/`, { headers })
  .then(res => res.json())
  .then(data => console.log(data));

3. Create a Review Cycle

Start a 360 review programmatically:

curl -X POST 'https://www.blik360.com/api/v1/cycles/' \
  -H 'Authorization: Bearer YOUR_TOKEN_HERE' \
  -H 'Content-Type: application/json' \
  -d '{
    "reviewee": "7a44880e-2f99-4593-b3a7-58109af8a468",
    "questionnaire": "f3d7c2a1-8b9e-4f5a-9c1d-2e3f4a5b6c7d",
    "reviewer_emails": {
      "self": ["[email protected]"],
      "peer": ["[email protected]", "[email protected]"],
      "manager": ["[email protected]"]
    },
    "send_invitations": true
  }'
payload = {
    "reviewee": "7a44880e-2f99-4593-b3a7-58109af8a468",
    "questionnaire": "f3d7c2a1-8b9e-4f5a-9c1d-2e3f4a5b6c7d",
    "reviewer_emails": {
        "self": ["[email protected]"],
        "peer": ["[email protected]", "[email protected]"],
        "manager": ["[email protected]"]
    },
    "send_invitations": True
}

response = requests.post(
    f"{BASE_URL}/cycles/",
    headers=headers,
    json=payload
)
cycle = response.json()
print(f"Created cycle {cycle['uuid']}")
const payload = {
  reviewee: '7a44880e-2f99-4593-b3a7-58109af8a468',
  questionnaire: 'f3d7c2a1-8b9e-4f5a-9c1d-2e3f4a5b6c7d',
  reviewer_emails: {
    self: ['[email protected]'],
    peer: ['[email protected]', '[email protected]'],
    manager: ['[email protected]']
  },
  send_invitations: true
};

fetch(`${BASE_URL}/cycles/`, {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/json' },
  body: JSON.stringify(payload)
})
  .then(res => res.json())
  .then(data => console.log(`Created cycle ${data.uuid}`));

API Reference

Core endpoints and operations

Reviewees

Manage people being reviewed

Method Endpoint Description
GET /api/v1/reviewees/ List all reviewees
POST /api/v1/reviewees/ Create reviewee
GET /api/v1/reviewees/{uuid}/ Get reviewee details
PUT /api/v1/reviewees/{uuid}/ Update reviewee
DELETE /api/v1/reviewees/{uuid}/ Soft-delete reviewee

Review Cycles

Manage 360 feedback cycles

Method Endpoint Description
GET /api/v1/cycles/ List all cycles
POST /api/v1/cycles/ Create cycle with reviewers
GET /api/v1/cycles/{uuid}/ Get cycle details
GET /api/v1/cycles/{uuid}/progress/ Get completion stats
POST /api/v1/cycles/{uuid}/complete/ Mark as complete & generate report

Questionnaires

Access available questionnaire templates

Method Endpoint Description
GET /api/v1/questionnaires/ List available questionnaires
GET /api/v1/questionnaires/{uuid}/ Get full questionnaire with questions

Reports

Access generated feedback reports

Method Endpoint Description
GET /api/v1/reports/ List all reports
GET /api/v1/reports/{uuid}/ Get report data with access token
POST /api/v1/reports/{uuid}/regenerate/ Regenerate report with fresh data

Webhooks

Real-time event notifications

Setting Up Webhooks

Configure webhook endpoints in Settings → Webhooks to receive HTTP POST requests when events occur.

Available Events

  • cycle.created - Fired when a new review cycle starts
  • cycle.completed - Fired when a review cycle is marked as complete
  • feedback.submitted - Fired when a reviewer completes feedback
  • report.generated - Fired when a report is generated

Webhook Payload Structure

Modern webhook format with event metadata:

{
  "id": "462d748a-d108-46ad-be98-ef3a6de5ccc3",
  "event": "cycle.created",
  "created": "2025-11-04T22:13:34Z",
  "data": {
    "cycle_uuid": "a8b7c6d5-e4f3-4a2b-9c1d-8e7f6a5b4c3d",
    "reviewee": {
      "uuid": "7a44880e-2f99-4593-b3a7-58109af8a468",
      "name": "John Doe",
      "email": "[email protected]"
    },
    "questionnaire": {
      "uuid": "f3d7c2a1-8b9e-4f5a-9c1d-2e3f4a5b6c7d",
      "name": "Software Engineering 360"
    },
    "created_at": "2025-11-04T22:13:34Z"
  }
}

Webhook Headers

Every webhook request includes these headers:

Header Description
X-Blik-Event Event type (e.g., "cycle.created")
X-Blik-Signature HMAC-SHA256 signature for verification
X-Blik-Delivery Unique delivery UUID (non-enumerable)

Verifying Webhook Signatures

Always verify the HMAC signature to ensure the webhook came from Blik:

import hmac
import hashlib

def verify_webhook(payload, signature_header, secret):
    """Verify webhook HMAC-SHA256 signature."""
    if not signature_header.startswith('sha256='):
        return False

    expected_sig = signature_header[7:]  # Remove 'sha256=' prefix
    computed_sig = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(expected_sig, computed_sig)

# Usage in Flask/Django
payload = request.body.decode('utf-8')
signature = request.headers.get('X-Blik-Signature')
secret = 'your_webhook_secret'

if verify_webhook(payload, signature, secret):
    # Process webhook
    data = json.loads(payload)
    print(f"Received {data['event']} event")
const crypto = require('crypto');

function verifyWebhook(payload, signatureHeader, secret) {
  if (!signatureHeader || !signatureHeader.startsWith('sha256=')) {
    return false;
  }

  const expectedSig = signatureHeader.slice(7); // Remove 'sha256=' prefix
  const computedSig = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(expectedSig),
    Buffer.from(computedSig)
  );
}

// Usage in Express
app.post('/webhook', (req, res) => {
  const payload = JSON.stringify(req.body);
  const signature = req.headers['x-blik-signature'];
  const secret = 'your_webhook_secret';

  if (verifyWebhook(payload, signature, secret)) {
    console.log(`Received ${req.body.event} event`);
    res.status(200).send('OK');
  } else {
    res.status(401).send('Invalid signature');
  }
});

Error Handling

Standard HTTP status codes and error responses

HTTP Status Codes

Status Meaning
200 OK Request successful
201 Created Resource created successfully
400 Bad Request Invalid request data or validation error
401 Unauthorized Missing or invalid authentication token
403 Forbidden Insufficient permissions for this operation
404 Not Found Resource doesn't exist or not in your organization
500 Server Error Internal server error (contact support)

Error Response Format

All error responses follow this consistent format:

{
  "detail": "Authentication credentials were not provided.",
  "error_code": "authentication_failed"
}

// Validation errors include field details:
{
  "reviewee": ["This field is required."],
  "questionnaire": ["Invalid uuid - object does not exist."],
  "error_code": "validation_error"
}

Need Help?

We're here to help you integrate successfully

Interactive Docs

Try the API directly in your browser with our Swagger documentation.

Open Swagger Docs

GitHub Discussions

Ask questions, share integrations, and get help from the community.

Join Discussion

Report Issues

Found a bug or have a feature request? Open an issue on GitHub.

Report Issue