Webhook Events
Full reference for every event type Easy2257 delivers, with exact payload shapes.
Every event shares the same envelope. Only data changes:
{
"id": "evt_m2k9x1_a7f3qz",
"type": "solo_account.verified",
"created": 1714000000,
"data": { }
}id is unique per event. Deduplicate on it: a delivery may arrive more than once. created is Unix seconds.
Identifiers in payloads (accountId, contentLogId, depictionId, scene and publication ids, productionId) are opaque strings with no prefix and no fixed structure. Store them as-is. Do not parse or validate a shape.
Solo accounts
solo_account.verified
The creator is fully compliant: subscription paid and government ID verified. Unlock uploads on this event.
{
"accountId": "cmd4k2x9p0001qz8h3v7ftg2a",
"externalUserId": "user_12345",
"status": "verified",
"verifiedAt": "2026-04-22T12:00:00Z"
}The status value is the literal string "verified", and the account key is accountId. A handler that reads data.id or compares status to "active" will never match. "active" is the status you get back from the REST API; "verified" is the status carried on this event.
It fires from five situations, all of which mean the same thing to you:
- The creator finished ID verification and their subscription is paid.
- An existing Easy2257 user approved your link request and was already an active solo creator.
- A lapsed subscription renewed and the account reactivated.
- You reactivated a creator you had deactivated (
POST /api/v1/solo-accounts/{accountId}/reactivate). - You re-called
POST /v1/solo-accountsfor an account that was already active, so your state can resync.
solo_account.suspended
The creator is no longer in custody. Revoke upload access. reason says why, and why decides what brings them back.
{
"accountId": "cmd4k2x9p0001qz8h3v7ftg2a",
"externalUserId": "user_12345",
"reason": "subscription_lapsed"
}reason | What happened | What brings them back |
|---|---|---|
subscription_lapsed | The creator's own subscription ended. | The creator pays again. |
platform_lapsed | Your platform fee lapsed, so every creator your platform funds was suspended. | Your platform fee is paid. |
partner_deactivated | You deactivated the creator, through POST /api/v1/solo-accounts/{accountId}/deactivate or the portal roster. | POST .../reactivate, from you. |
platform_deactivated, manual_suspension | Easy2257 suspended the account. | Easy2257. |
When they come back and the creator's ID verification is still on file, the account reactivates and you receive solo_account.verified again. No re-verification, and no second onboarding trip for the creator.
Content log
content_log.created
A depiction was recorded against the creator's identity record.
{
"accountId": "cmd4k2x9p0001qz8h3v7ftg2a",
"externalContentId": "video_abc123",
"contentLogId": "cmd4k7f2r0003qz8hb1n4pd9c",
"depictionId": "cmd4k7f2r0004qz8hs6y2wk1e",
"contentUrl": "https://your-cdn.com/video_abc123.mp4",
"contentType": "video",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"complianceCopy": "pending"
}externalContentId and sha256 are null when you did not send them. complianceCopy is pending when you declared a copy on the create call and have not confirmed it yet, missing when you did not declare one (JuicyStarz pipeline only; platform-plan partners must), and stored once confirmed.
content_log.copy_stored
The compliance copy of a depiction was uploaded, verified against the declared size and type, and hashed by Easy2257 (28 CFR 75.2(a)(1)(iii)). Fires once per depiction, after your POST .../content/{externalContentId}/compliance-copy call.
{
"accountId": "cmd4k2x9p0001qz8h3v7ftg2a",
"externalContentId": "video_abc123",
"contentLogId": "cmd4k7f2r0003qz8hb1n4pd9c",
"depictionId": "cmd4k7f2r0004qz8hs6y2wk1e",
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"bytes": 48213377,
"mimeType": "video/mp4",
"storedAt": "2026-08-21T18:04:11.000Z"
}sha256 here is computed by Easy2257 from the stored copy; it is not the sha256 you sent on the create call, which describes your original.
content_log.deleted
A logged depiction was removed, either by your DELETE call or by an Easy2257-side takedown you did not initiate.
{
"accountId": "cmd4k2x9p0001qz8h3v7ftg2a",
"externalContentId": "video_abc123",
"contentLogId": "cmd4k7f2r0003qz8hb1n4pd9c",
"depictionId": "cmd4k7f2r0004qz8hs6y2wk1e",
"contentUrl": "https://your-cdn.com/video_abc123.mp4",
"contentType": "video"
}There is no sha256 on the delete event. Match on externalContentId or depictionId.
When the takedown is Easy2257-side (an administrator actioned a depicted-person removal request against this depiction), the event carries three extra fields so your receiver can tell it from your own DELETE and log the basis:
{
"reason": "removal_request",
"removalRequestId": "cmd4m1q7h0009qz8hx2c5tb4f",
"legalBasis": "NCII_TAKE_IT_DOWN"
}legalBasis is one of NCII_TAKE_IT_DOWN, NCII_STATE, AN_5196_APPEAL, DMCA, OTHER. Matching a removal request to a logged depiction is confirmed by an Easy2257 administrator before this event is sent; it is never dispatched by automatic URL matching alone.
Treat this event as authoritative even when you did not trigger it. Under the TAKE IT DOWN Act, pull the content from your CDN within 48 hours.
Collab scenes
collab_scene.created
The scene exists and invite emails have gone out. This is not permission to publish.
{
"scene": {
"sceneId": "cmd4kb8u10007qz8h9x3mrt5v",
"status": "pending_signatures",
"performers": [
{ "email": "performer@example.com", "status": "pending_consent" },
{ "email": "newcomer@example.com", "status": "pending_consent" }
],
"fees": {
"sceneFeeCents": 500,
"onboardingFeeCents": 2500,
"totalCents": 3000,
"charged": true,
"stripeChargeId": "pi_3ABC...",
"chargeError": null
},
"expiresAt": "2026-05-06T12:00:00Z"
},
"uploadSessionId": null
}Every invited performer starts at pending_consent, whether or not the address already had an Easy2257 account. Registration and ID verification, when needed, happen inside the magic-link flow and are never visible here, so performer entries cannot be used to probe whether an email is already on Easy2257. The onboarding fee appears only as a scene-level aggregate.
If the immediate fee charge failed, fees.charged is false and fees.chargeError carries the failure code. The scene is created and consent collection proceeds either way.
uploadSessionId is the hosted attestation session that produced the scene, so you can correlate the event back to the content row that triggered it. It is null when you created the scene directly through POST /v1/collab-scenes.
collab_scene.performer_consented
One performer signed. Others may still be outstanding.
{
"scene": { "id": "cmd4kb8u10007qz8h9x3mrt5v" },
"performer": { "email": "performer@example.com", "consentedAt": "2026-04-23T09:14:00Z" },
"uploadSessionId": null
}uploadSessionId is not always present on this event. It is null when the scene was created directly through POST /v1/collab-scenes, and the key is absent entirely when the signer was a new performer who completed registration inside the magic-link flow. Treat it as optional and correlate on scene.id, which every delivery carries.
collab_scene.performer_refused
A performer declined. The scene moves to blocked and can never be greenlit.
{
"scene": { "id": "cmd4kb8u10007qz8h9x3mrt5v", "status": "blocked" },
"performer": { "email": "performer@example.com", "reason": "not_me" }
}reason is one of not_me, declining, or other.
collab_scene.greenlit
Every performer has consented and signed. Publish on this event, not before.
{
"scene": {
"id": "cmd4kb8u10007qz8h9x3mrt5v",
"status": "greenlit",
"greenlitAt": "2026-04-23T09:14:00Z"
},
"uploadSessionId": null
}uploadSessionId behaves exactly as on collab_scene.performer_consented: null for scenes created directly through the API, and absent entirely when the closing signature came from a newly registered performer. Correlate on scene.id.
collab_scene.blocked
The scene reached its 14-day expiry with performers still unsigned. A reminder went out at day 7.
{
"scene": { "id": "cmd4kb8u10007qz8h9x3mrt5v", "status": "blocked", "reason": "sla_breach" },
"unsignedPerformers": ["newcomer@example.com"]
}collab_scene.cancelled
The uploader cancelled the scene through POST /v1/collab-scenes/{id}/cancel.
{
"scene": { "id": "cmd4kb8u10007qz8h9x3mrt5v", "status": "cancelled" }
}collab_scene.publication_created
You own the scene, and a different partner platform registered a publication of it. Fires only to the scene owner.
{
"scene": { "id": "cmd4kb8u10007qz8h9x3mrt5v" },
"publication": {
"id": "cmd4kd1s7000bqz8hc4v8jq2m",
"partnerId": "cmd4jz0aa0000qz8h1t6knw3p",
"publishedAt": "2026-04-24T18:02:00Z"
}
}collab_scene.publication_removed
A partner recorded a takedown of their publication of your scene.
{
"scene": { "id": "cmd4kb8u10007qz8h9x3mrt5v" },
"publication": {
"id": "cmd4kd1s7000bqz8hc4v8jq2m",
"partnerId": "cmd4jz0aa0000qz8h1t6knw3p",
"removedAt": "2026-05-02T10:30:00Z",
"reason": "performer_request"
}
}Upload sessions
upload_session.solo_completed
A creator finished a hosted attestation session for single-performer content. The attestation block is the evidence record: IP, user agent, timestamp, and the exact wording they agreed to.
{
"sessionId": "cmd4kf9x2000fqz8hp7r3zd8k",
"contentId": "video_abc123",
"contentType": "video",
"uploaderExternalUserId": "user_12345",
"attestation": {
"ip": "203.0.113.42",
"ua": "Mozilla/5.0 ...",
"attestedAt": "2026-04-24T17:55:00Z",
"text": "I confirm I am the only person depicted ...",
"shootDate": "2026-04-20T00:00:00Z"
}
}Productions
production.documented
A producer finalized documentation, or reissued the sealed package after a correction. certificate is the signed RS256 JWT itself: store the string alongside your content and verify it offline against easy2257.com/.well-known/jwks.json. A second delivery for the same productionId means the record was corrected and the package rebuilt: the certificate on the newer delivery is the one that stands, and the one it replaces shows as SUPERSEDED rather than revoked, so it never reaches the revocation list.
{
"productionId": "cmd4kh2m3000jqz8hn9w5vb7t",
"performerCount": 3,
"documentedAt": "2026-04-25T14:00:00Z",
"certificate": "eyJhbGciOiJSUzI1NiIsImtpZCI6..."
}production.takedown_requested
A depicted person submitted a removal request against a production linked to you. Act by removalDeadline.
{
"productionId": "cmd4kh2m3000jqz8hn9w5vb7t",
"removalRequestId": "cmd4kj5p1000nqz8hd2f6xs4y",
"legalBasis": "NCII_TAKE_IT_DOWN",
"performerEmailHash": "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
"requestedAt": "2026-04-26T08:00:00Z",
"removalDeadline": "2026-04-28T08:00:00Z"
}No raw address is ever sent, and despite the name, performerEmailHash is the SHA-256 hex digest of the lowercased, trimmed address the claimant filed the removal request with. The claimant is whoever submitted the request: often the depicted person, but possibly a parent, attorney, or other representative, and possibly the performer using a different address than the one you hold. Hashing your own records and comparing digests is a correlation hint, not an identifier, and a miss does not mean the request is invalid. productionId and removalDeadline are what govern the takedown.
removalDeadline is already computed for the legal basis of the request, so honor the field rather than hardcoding a window:
legalBasis | Window |
|---|---|
NCII_TAKE_IT_DOWN | 48 hours |
NCII_STATE | 72 hours |
AN_5196_APPEAL, DMCA, OTHER | 7 business days |
production.certificate_renewed
Annual re-attestation issued a fresh certificate. Replace your stored copy: the previous one is superseded (it shows as SUPERSEDED in GET /v1/productions/{id}/certificates), the content stays up, and it does not appear in the revocation list.
{
"productionId": "cmd4kh2m3000jqz8hn9w5vb7t",
"newCertificate": "eyJhbGciOiJSUzI1NiIsImtpZCI6...",
"previousCertificateExpiresAt": "2027-04-25T14:00:00Z"
}production.records_inspected
A federal inspector accessed the records for that production. Informational: no action required from you, because Easy2257 responded as Custodian of Records.
{
"productionId": "cmd4kh2m3000jqz8hn9w5vb7t",
"inspectedAt": "2026-06-01T16:20:00Z",
"inspectorAgency": "FBI"
}Scene verification jobs
These three fire for scenes created through the scene verification job flow, not the Solo Creator or Collab Scene surfaces. They share one payload shape, where verification.id is the job id you supplied and verification.contentId is your content reference.
performer.id_verified
{
"verification": { "id": "job_abc123", "contentId": "video_abc123" },
"performer": { "email": "performer@example.com", "status": "id_verified" }
}performer.forms_signed
{
"verification": { "id": "job_abc123", "contentId": "video_abc123" },
"performer": { "email": "performer@example.com", "status": "forms_signed" }
}verification.cancelled
The scene behind the job was cancelled.
{
"verification": { "id": "job_abc123", "contentId": "video_abc123", "status": "cancelled" }
}This event has nothing to do with solo onboarding. No event fires when a creator abandons solo onboarding. Poll GET /v1/solo-accounts/{accountId} if you want to know whether someone is still pending, and re-send them to the onboardingUrl that call returns.
Testing
test.ping
Fired by the test button in the partner portal. Signed exactly like a live event, with the delivery header set to del_test and an id of the form evt_test_<timestamp>.
{
"message": "This is a test webhook from Easy2257"
}