Gamify Loyalty Webhooks

Webhooks let you receive real-time HTTP notifications when something interesting happens in the Gamify Loyalty System: a mission is completed, a reward is claimed, a badge is earned. Optimove posts a JSON payload to a URL you control; your server validates it and reacts however you need.

Use a webhook when you want to:

  • Mirror loyalty events into your CRM, data warehouse, or marketing tool
  • Trigger downstream automations (emails, push notifications, SMS)
  • Audit player activity in your own systems

Setting Up a Webhook

Open Settings → Webhook Configuration and click Add Webhook.

Form Fields

FieldRequiredDescription
NameYesShort, human-readable label (1–128 characters). Used only inside the Optimove UI.
URLYesFully qualified HTTPS endpoint on your server.
DescriptionNoOptional notes for your team.
EventsYesOne or more event types to receive (see

Validating the URL

Click Validate to verify reachability. Optimove sends a sample mission_completed payload to the URL with a 10-second timeout. The UI shows the HTTP status code returned by your endpoint.

ℹ️

No x-api-key is included in the Validate request, so you can safely point it at third-party listeners (webhook.site, requestbin, ngrok) without exposing your production key.

Note that x-api-key is sent on every real delivery. If you used a test URL for validation, switch it back to your own endpoint before saving — otherwise the production key will be sent to the third-party listener.

Saving and Capturing the API Key

Click Save & Activate. A modal displays the generated x-api-key for this webhook.

⚠️

Copy and store this value now — it is shown only once. After you close the modal, the key cannot be retrieved. You can only rotate it to generate a new one.

Managing the API Key

Every webhook gets a unique 64-character hex secret. Optimove attaches it as the x-api-key request header on every real delivery.

Validating on Your Server

  1. Read the x-api-key header on each incoming request.
  2. Compare it against the value you stored during creation. Use a constant-time comparison to avoid timing attacks.
  3. Reject any request whose header is missing or does not match.

How Optimove Stores the Key

The key is encrypted at rest with AES-256-GCM. Optimove never logs or returns the plaintext after creation — the only way to see a value again is to rotate.

Rotating the Key

Rotate if the current key is compromised, or on a regular schedule:

  1. Open the webhook in Settings → Webhook Configuration and click Edit.
  2. Click Rotate API key and confirm in the modal.
  3. A new plaintext key appears in the same one-time modal. Copy it immediately.
⚠️

The old key stops working immediately. Update the value on your receiving server before the next event arrives, or deliveries will start failing authentication.

Managing Existing Webhooks

Editing

Change Name, URL, Description, Events, or brand scope at any time. The API key is not rotated by editing — only Rotate API key changes it.

Disabling

Toggle the webhook to inactive. No further events are sent, but the configuration and key are preserved. Re-enable at any time.

Deleting

Permanently removes the webhook and its API key. This cannot be undone.

Outgoing Request Format

Every real delivery is an HTTPS POST.

Headers

HeaderExample ValueNotes
Content-Typeapplication/jsonAlways JSON.
X-Webhook-Event-Typemission_completedUse this to route the request without parsing the body.
X-Webhook-Delivery-Ida1b2c3d4-e5f6-7890-abcd-ef1234567890Unique per delivery attempt. Use for idempotency and logging.
x-api-key9c8b7a6d… (64 hex chars)Per-webhook shared secret. Validate on every request.

Body

The body is the event payload directly: no envelope, no nested payload field. The event type is in the X-Webhook-Event-Type header, not the body.

Delivery Semantics

Timeouts and Success

Optimove waits up to 10 seconds for your server to respond. Any HTTP 2xx status counts as delivered.

Retries

A delivery is retried only on transient failures:

  • HTTP status 408, 429, 500, 502, 503, or 504
  • Request timeout
  • Network or connection error

Up to 5 retries with increasing delays:

  1. 2 seconds
  2. 8 seconds
  3. 30 seconds
  4. 2 minutes
  5. 10 minutes

After the final retry, the delivery is dropped.

ℹ️

Other 4xx responses (400, 401, 403, 404, etc.) are not retried — they are recorded as failures immediately. Make sure your endpoint returns one of the retryable codes (or lets the request time out) when you need Optimove to back off and retry.

Ordering and At-Least-Once

Deliveries are not strictly ordered. Use created_at in the payload if you need to reconstruct sequence. Because of retries, your endpoint may receive the same X-Webhook-Delivery-Id more than once — make your handlers idempotent.

Event Reference

Common Payload Fields

All payloads share these base fields:

FieldTypeDescription
tenant_idnumberOptimove tenant identifier.
brand_idstringBrand identifier the event belongs to. Treat as an opaque string.
player_idstringPlayer identifier the event is about. Treat as an opaque string — Optimove does not enforce a specific format.
created_atnumberUnix epoch milliseconds when the event occurred.

Event-specific fields are listed under each event below.

mission_completed

Sent when a player finishes all objectives of a mission.

Event-specific fields:

FieldTypeDescription
objective_idstringUnique identifier of the completed mission.
statusstringCompletion status, for example "completed".

Sample payload:

{
  "tenant_id": 125,
  "brand_id": "3f7a9c12-8b4e-4d2a-9f1c-6e5b8a2d4c11",
  "player_id": "8d2e1f4a-7b6c-4d5e-9f8a-1b2c3d4e5f6a",
  "created_at": 1747665138000,
  "objective_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "completed"
}

reward_claimed_from_mission

Sent when a player claims the reward attached to a completed mission.

Event-specific fields:

FieldTypeDescription
objective_idstringUnique identifier of the source mission.
rewardsarrayList of reward objects. Each object contains rewardTypeId (string), playerId (string), and amount (number).

Sample payload:

{
  "tenant_id": 125,
  "brand_id": "3f7a9c12-8b4e-4d2a-9f1c-6e5b8a2d4c11",
  "player_id": "8d2e1f4a-7b6c-4d5e-9f8a-1b2c3d4e5f6a",
  "created_at": 1747665302000,
  "objective_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "rewards": [
    {
      "rewardTypeId": "9c8b7a6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
      "playerId": "8d2e1f4a-7b6c-4d5e-9f8a-1b2c3d4e5f6a",
      "amount": 100
    }
  ]
}

reward_claimed_from_leaderboard

Sent when a player claims a reward earned through a leaderboard placement.

Event-specific fields:

FieldTypeDescription
leaderboard_idstringUnique identifier of the source leaderboard.
rewardsarrayList of reward objects. Same shape as reward_claimed_from_mission.

Sample payload:

{
  "tenant_id": 125,
  "brand_id": "3f7a9c12-8b4e-4d2a-9f1c-6e5b8a2d4c11",
  "player_id": "8d2e1f4a-7b6c-4d5e-9f8a-1b2c3d4e5f6a",
  "created_at": 1747670445000,
  "leaderboard_id": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
  "rewards": [
    {
      "rewardTypeId": "7d6c5b4a-3e2f-1a0b-9c8d-7e6f5a4b3c2d",
      "playerId": "8d2e1f4a-7b6c-4d5e-9f8a-1b2c3d4e5f6a",
      "amount": 50
    }
  ]
}

badge_earned

Sent when a player earns a badge.

Event-specific fields:

FieldTypeDescription
badge_idstringUnique identifier of the badge.
badge_namestringDisplay name of the badge, for example "Gold Explorer".

Sample payload:

{
  "tenant_id": 125,
  "brand_id": "3f7a9c12-8b4e-4d2a-9f1c-6e5b8a2d4c11",
  "player_id": "8d2e1f4a-7b6c-4d5e-9f8a-1b2c3d4e5f6a",
  "created_at": 1747674729000,
  "badge_id": "c4d5e6f7-a8b9-0123-cdef-456789012345",
  "badge_name": "Gold Explorer"
}

virtual_currency_added

Sent when virtual currency is credited to a player.

Event-specific fields:

FieldTypeDescription
currency_idstringUnique identifier of the virtual currency type.
amountnumberAmount of virtual currency credited.

Sample payload:

{
  "tenant_id": 125,
  "brand_id": "3f7a9c12-8b4e-4d2a-9f1c-6e5b8a2d4c11",
  "player_id": "8d2e1f4a-7b6c-4d5e-9f8a-1b2c3d4e5f6a",
  "created_at": 1747676730000,
  "currency_id": "d5e6f7a8-b9c0-1234-def0-567890123456",
  "amount": 500
}

additional_reward_claimed

Sent when a player claims a standalone (additional) reward. Includes the source it originated from.

Event-specific fields:

FieldTypeDescription
reward_idstringUnique identifier of the reward.
reward_namestringDisplay name of the reward, for example "Bonus Points".
short_codestringOptional short code associated with the reward, for example "BONUS50".
sourcestringOrigin of the reward: "mission" or "leaderboard".
objective_idstringPresent when source is "mission". Unique identifier of the source mission.
leaderboard_idstringPresent when source is "leaderboard". Unique identifier of the source leaderboard.

Sample payload:

{
  "tenant_id": 125,
  "brand_id": "3f7a9c12-8b4e-4d2a-9f1c-6e5b8a2d4c11",
  "player_id": "8d2e1f4a-7b6c-4d5e-9f8a-1b2c3d4e5f6a",
  "created_at": 1747679211000,
  "reward_id": "5b4a3c2d-1e0f-9a8b-7c6d-5e4f3a2b1c0d",
  "reward_name": "Bonus Points",
  "short_code": "BONUS50",
  "source": "mission",
  "objective_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

Security Checklist

Before going live, verify your endpoint meets these requirements:

  • Serve the endpoint over HTTPS only.
  • Validate the x-api-key header on every request; reject requests where the header is missing or does not match.
  • Never log the x-api-key value or include it in error reports.
  • Treat deliveries as at-least-once: deduplicate on X-Webhook-Delivery-Id (or a domain-specific key) before applying side effects.
  • Respond quickly — well under 10 seconds. If your processing takes longer, acknowledge with 200 immediately and queue the work.