Skip to main content
Stringhive Payload Reference

Payload Reference

Every webhook request from Stringhive has the same set of headers and a JSON body.

Headers

Content-Type:           application/json
User-Agent:             Stringhive-Webhook/1.0
X-Stringhive-Event:     string.created
X-Stringhive-Delivery:  a1b2c3d4-e5f6-...
X-Stringhive-Signature: sha256=abc123...

X-Stringhive-Delivery is a unique ID for each delivery attempt. X-Stringhive-Signature is only present if you configured a secret.

Verifying the signature

If you set a secret, verify the signature before processing the payload. Stringhive uses HMAC-SHA256.

// PHP
$payload = file_get_contents('php://input');
$signature = 'sha256=' . hash_hmac('sha256', $payload, $secret);
$trusted = hash_equals($signature, $_SERVER['HTTP_X_STRINGHIVE_SIGNATURE']);
// Node.js
const crypto = require('crypto');
const signature = 'sha256=' + crypto
  .createHmac('sha256', secret)
  .update(body)
  .digest('hex');
const trusted = crypto.timingSafeEqual(
  Buffer.from(signature),
  Buffer.from(req.headers['x-stringhive-signature'])
);

Always use a timing-safe comparison. Regular string comparison is vulnerable to timing attacks.

Payload: string.created

{
  "event": "string.created",
  "hive": { "slug": "my-app", "name": "My App" },
  "string": {
    "key": "nav.home",
    "source_value": "Home",
    "file": "nav.php",
    "is_plural": false
  },
  "user": { "id": "...", "name": "Daniel" },
  "timestamp": "2025-06-01T12:00:00Z"
}

Payload: string.updated

{
  "event": "string.updated",
  "hive": { "slug": "my-app", "name": "My App" },
  "string": {
    "key": "nav.home",
    "source_value": "Home page",
    "previous_value": "Home",
    "file": "nav.php",
    "is_plural": false
  },
  "user": { "id": "...", "name": "Daniel" },
  "timestamp": "2025-06-01T12:00:00Z"
}

Payload: string.deleted

{
  "event": "string.deleted",
  "hive": { "slug": "my-app", "name": "My App" },
  "string": { "key": "nav.old_page", "file": "nav.php" },
  "user": { "id": "...", "name": "Daniel" },
  "timestamp": "2025-06-01T12:00:00Z"
}

Payload: translation.updated

{
  "event": "translation.updated",
  "hive": { "slug": "my-app", "name": "My App" },
  "string": { "key": "nav.home", "file": "nav.php" },
  "translation": {
    "locale": "de",
    "value": "Startseite",
    "state": "translated"
  },
  "user": { "id": "...", "name": "Maria" },
  "timestamp": "2025-06-01T12:00:00Z"
}

Payload: translation.approved

{
  "event": "translation.approved",
  "hive": { "slug": "my-app", "name": "My App" },
  "string": { "key": "nav.home", "file": "nav.php" },
  "translation": {
    "locale": "de",
    "value": "Startseite",
    "state": "approved"
  },
  "user": { "id": "...", "name": "Daniel" },
  "timestamp": "2025-06-01T12:00:00Z"
}

Payload: comment.created

Fires when someone leaves a comment on a string. The locale field is present if the comment was left in the context of a specific locale, and null if it was a general string comment.

{
  "event": "comment.created",
  "hive": { "slug": "my-app", "name": "My App" },
  "string": { "key": "checkout.submit", "file": "checkout.php" },
  "comment": {
    "body": "The German here sounds too formal, consider using du-form.",
    "locale": "de"
  },
  "user": { "id": "...", "name": "Maria" },
  "timestamp": "2025-06-01T12:00:00Z"
}

Payload: locale.completed

Fires when the last unapproved string in a Hive gets approved for a locale. Every single string is now approved. Pop the champagne.

{
  "event": "locale.completed",
  "hive": { "slug": "my-app", "name": "My App" },
  "locale": "de",
  "string_count": 142,
  "timestamp": "2025-06-01T12:00:00Z"
}

This event fires once per locale per "completion". If a string is later unapproved and re-approved, it fires again.