Integrate 360° feedback into your workflow with our REST API and webhooks
Everything you need to integrate Blik into your systems
Bearer token authentication with organization-scoped access. Create API tokens in your dashboard.
Full CRUD operations for cycles, reviewees, questionnaires, and reports. Standard HTTP methods with JSON responses.
Real-time event notifications with HMAC signatures. Subscribe to cycle.created, cycle.completed, feedback.submitted, and report.generated events.
Interactive Swagger documentation at /api/v1/docs/. Test endpoints directly in your browser.
No rate limits, no usage caps, no hidden fees. Unlimited API calls included with your subscription.
Modern webhook structure with non-enumerable UUIDs. HMAC-SHA256 signatures for webhook verification.
Get started in 5 minutes
Navigate to Settings → API Tokens in your dashboard and create a new token with the required permissions.
Save the token value immediately—it's only shown once.
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));
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}`));
Core endpoints and operations
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 |
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 |
Access available questionnaire templates
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/v1/questionnaires/ |
List available questionnaires |
GET |
/api/v1/questionnaires/{uuid}/ |
Get full questionnaire with questions |
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 |
Real-time event notifications
Configure webhook endpoints in Settings → Webhooks to receive HTTP POST requests when events occur.
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
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"
}
}
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) |
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');
}
});
Standard HTTP status codes and error responses
| 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) |
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"
}
We're here to help you integrate successfully
Try the API directly in your browser with our Swagger documentation.
Open Swagger DocsAsk questions, share integrations, and get help from the community.
Join Discussion