Skip to content
Back to developer docs
Outbound webhooks · contract v1

Score it. Forge it. Hear about it.

When a song is forged, scored, or gauntleted on SongForgeAI, we POST a signed JSON envelope to the URL you configured. HMAC-SHA256 signing, idempotent envelope ids, no SDK required.

Note: the dispatcher + event contract ship in this build. The per-user subscription UI is a follow-up ticket. Build your receiver against this doc; the “subscribe” button lights up when the UI lands.

Event types

TypeFires when
song.forgedForge pipeline finishes producing lyrics + style.
song.scoredEval pipeline writes a composite score (includes the reproducibility seal).
gauntlet.completedGauntlet refinement finishes. Carries pre + post scores + improved flag.

Currently exported: song.scored, song.forged, gauntlet.completed

Envelope shape

{
  "apiVersion": "v1",
  "id": "11111111-2222-3333-4444-555555555555",
  "emittedAt": "2026-04-25T08:00:00.000Z",
  "event": {
    "type": "song.scored",
    "songId": "song-abc",
    "userId": "user-xyz",
    "compositeScore": 82,
    "grade": "A",
    "percentile": "Top 12%",
    "seal": {
      "rubricVersion": "1.0.1",
      "model": "claude-sonnet-4-20250514",
      "temperature": 0.7,
      "buildSha": "a1b2c3d4...",
      "build": 1215
    }
  }
}

Signing & verification

Every request carries X-SongForge-Signature set to sha256=<hex>where the hex is HMAC-SHA256 of the raw request body keyed by your subscriber secret. Verify with a constant-time compare:

import { createHmac, timingSafeEqual } from 'node:crypto';

function verifyWebhook(rawBody: string, header: string, secret: string): boolean {
  const expected = 'sha256=' + createHmac('sha256', secret)
    .update(rawBody, 'utf8')
    .digest('hex');
  // Constant-time compare to defeat timing attacks.
  const a = Buffer.from(header);
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}

Headers

X-SongForge-EventThe event.type, e.g. song.scored.
X-SongForge-EnvelopeUUID of this envelope. Use as your idempotency key.
X-SongForge-Api-VersionCurrently "v1".
X-SongForge-Build-ShaGit SHA of the deploy that emitted the event.
X-SongForge-Signaturesha256=<hex>. Omitted when subscriber secret is empty (test mode only).

Idempotency contract

The envelope id is stable across re-deliveries. If your handler succeeded once, store the id and short-circuit subsequent calls with the same id. Future retry logic (Tier B #22) will produce duplicate deliveries deliberately when network failures hide our success.