Webhooks
Webhooks push new content to you instead of making you poll for it. There is one subscribable
event type, content_view.item_added, and it is deliberately general: a
Content View is a saved filter, so any
condition you can express as a view — a platform, an approval status, an engagement threshold, an
AI Filter verdict — becomes a webhook trigger by pointing a subscription at that view. When an
item newly matches a subscribed view, Archive POSTs a signed JSON event to your HTTPS endpoint.
How events fire
An event’s identity is the (item, view) pair. Each pair fires at most once, ever — an item
that leaves and re-enters a view does not fire again. One item matching two of your subscribed
views produces two events; two subscriptions sharing one view produce one event delivered to each
endpoint separately (same event_id, each signed with its own secret).
Timing has two phases:
- First evaluation runs a few minutes after an item is captured, once transcription and enrichment have settled.
- Re-evaluation runs hourly for 14 days after the item’s post date. Views that filter on values arriving after capture — engagement thresholds, usage-rights status, AI Filters — still fire when the item crosses into the view later, with up to about an hour of lag.
When you subscribe to a view that already has matching items, those existing matches are recorded as events but not delivered — a new subscription never floods your endpoint with history. Only items matching from that point on are POSTed.
Delivery is at-least-once with no ordering guarantee: a later event can arrive before an
earlier one, and a retry can deliver the same event twice. Deduplicate on event_id and treat
arrival order as meaningless.
Create a subscription
createWebhookSubscription takes a name, the HTTPS endpoint, and the view ids to watch.
metadata is an optional object (up to 4 KB) echoed verbatim on every delivery — use it to route
events inside your systems:
mutation CreateWebhookSubscriptionDefault($input: CreateWebhookSubscriptionInput!) {
createWebhookSubscription(input: $input) {
webhookSubscription {
id
name
url
eventTypes
viewIds
metadata
status
}
secret
userErrors {
field
message
}
}
} {
"input": {
"name": "Fulfilment webhook",
"url": "https://hooks.northwind-botanicals.example/archive/new-content",
"viewIds": [
"eaa824b8-374f-5db8-bee2-51dfd8c3776c"
],
"eventTypes": [
"content_view.item_added"
],
"metadata": {
"team": "fulfilment"
}
}
} Authorization: Bearer docs_demo_token_0000000000000000000000
WORKSPACE-ID: 6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a curl https://app.archive.com/api/v2 \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer docs_demo_token_0000000000000000000000' \
-H 'WORKSPACE-ID: 6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a' \
-d '{
"query": "mutation CreateWebhookSubscriptionDefault($input: CreateWebhookSubscriptionInput!) {\n createWebhookSubscription(input: $input) {\n webhookSubscription {\n id\n name\n url\n eventTypes\n viewIds\n metadata\n status\n }\n secret\n userErrors {\n field\n message\n }\n }\n}",
"variables": {
"input": {
"name": "Fulfilment webhook",
"url": "https://hooks.northwind-botanicals.example/archive/new-content",
"viewIds": [
"eaa824b8-374f-5db8-bee2-51dfd8c3776c"
],
"eventTypes": [
"content_view.item_added"
],
"metadata": {
"team": "fulfilment"
}
}
}
}' const response = await fetch("https://app.archive.com/api/v2", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID": "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a"
},
body: JSON.stringify({
query: `mutation CreateWebhookSubscriptionDefault($input: CreateWebhookSubscriptionInput!) {
createWebhookSubscription(input: $input) {
webhookSubscription {
id
name
url
eventTypes
viewIds
metadata
status
}
secret
userErrors {
field
message
}
}
}`,
variables: {
"input": {
"name": "Fulfilment webhook",
"url": "https://hooks.northwind-botanicals.example/archive/new-content",
"viewIds": [
"eaa824b8-374f-5db8-bee2-51dfd8c3776c"
],
"eventTypes": [
"content_view.item_added"
],
"metadata": {
"team": "fulfilment"
}
}
}
})
})
console.log(JSON.stringify(await response.json(), null, 2)) require "json"
require "net/http"
query = <<~'GRAPHQL'
mutation CreateWebhookSubscriptionDefault($input: CreateWebhookSubscriptionInput!) {
createWebhookSubscription(input: $input) {
webhookSubscription {
id
name
url
eventTypes
viewIds
metadata
status
}
secret
userErrors {
field
message
}
}
}
GRAPHQL
response = Net::HTTP.post(
URI("https://app.archive.com/api/v2"),
{ query: query, variables: { "input" => { "name" => "Fulfilment webhook", "url" => "https://hooks.northwind-botanicals.example/archive/new-content", "viewIds" => ["eaa824b8-374f-5db8-bee2-51dfd8c3776c"], "eventTypes" => ["content_view.item_added"], "metadata" => { "team" => "fulfilment" } } } }.to_json,
"Content-Type" => "application/json",
"Authorization" => "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID" => "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a"
)
puts response.body import json
import requests
query = """
mutation CreateWebhookSubscriptionDefault($input: CreateWebhookSubscriptionInput!) {
createWebhookSubscription(input: $input) {
webhookSubscription {
id
name
url
eventTypes
viewIds
metadata
status
}
secret
userErrors {
field
message
}
}
}
"""
response = requests.post(
"https://app.archive.com/api/v2",
json={"query": query, "variables": {"input": {"name": "Fulfilment webhook", "url": "https://hooks.northwind-botanicals.example/archive/new-content", "viewIds": ["eaa824b8-374f-5db8-bee2-51dfd8c3776c"], "eventTypes": ["content_view.item_added"], "metadata": {"team": "fulfilment"}}}},
headers={
"Authorization": "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID": "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a",
},
)
print(json.dumps(response.json(), indent=2)) {
"data": {
"createWebhookSubscription": {
"webhookSubscription": {
"id": "b24c9093-72a3-504d-b97c-cb023efca979",
"name": "Fulfilment webhook",
"url": "https://hooks.northwind-botanicals.example/archive/new-content",
"eventTypes": [
"content_view.item_added"
],
"viewIds": [
"eaa824b8-374f-5db8-bee2-51dfd8c3776c"
],
"metadata": {
"team": "fulfilment"
},
"status": "ACTIVE"
},
"secret": "whsec_EXAMPLE_SECRET_SHOWN_ONCE_DO_NOT_USE",
"userErrors": []
}
}
} The endpoint URL must be https, on port 443 or an unprivileged port (≥1024), without embedded
credentials, and its host must resolve to a public IP — private and internal addresses are
rejected, and re-checked at every send. Creating an exact duplicate (same URL, event types, and
view ids as an existing subscription) returns a userError naming the existing subscription
rather than a second copy. Two subscriptions whose views merely overlap are allowed — and will
double-deliver items matching both, by design.
To pause a subscription without deleting it, set status: DISABLED_BY_USER via
updateWebhookSubscription; set ACTIVE to resume. Paused subscriptions accumulate nothing:
their views are simply not evaluated while paused. deleteWebhookSubscription is a hard delete
that also removes the subscription’s delivery history — the subscribed views survive.
The delivery request
Each event arrives as its own POST — there is no batching. The request carries:
| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | Archive-Webhooks/1.0 |
X-Archive-Signature | t=<unix-seconds>,v1=<hex-hmac> (two v1= values during secret rotation) |
X-Archive-Event-Id | The event UUID — your idempotency key |
X-Archive-Event-Type | content_view.item_added |
The body is a fixed eight-key envelope:
{
"event_id": "0f8e0d63-409e-5fd1-88bd-2febbe05fa53",
"event_type": "content_view.item_added",
"event_version": "1.0",
"created_at": "2026-08-14T09:30:12Z",
"workspace_id": "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a",
"subscription_id": "41662e09-10c4-52e4-844a-62ddedb38f11",
"subscription_metadata": { "team": "growth" },
"data": {
"item": {
"id": "12dedc45-e264-5d57-91f7-4d9aa7a05849",
"media_item_id": "7f52a5cd-72cb-5d20-9a3e-b1461cf6f4b2",
"provider": "tiktok",
"public_url": "https://www.tiktok.com/@maya.skincare/video/7300112233445566778",
"usage_rights_url": null,
"creator": {
"account_name": "maya.skincare",
"followers_count": 12800,
"verified": false
}
},
"view": {
"id": "eaa824b8-374f-5db8-bee2-51dfd8c3776c",
"name": "UGC — Approved"
}
}
}data.item.creator is null when the item has no associated creator. The payload is a compact
notification, not the full item — fetch anything else through the
items query using data.item.id.
Respond with any 2xx within 10 seconds. Anything else — including a redirect — counts as a failure and schedules a retry, so do the minimum (verify, enqueue, respond) and process the event asynchronously.
Verify the signature
Every delivery is signed with your subscription’s secret: HMAC-SHA256, hex-encoded, over the
string "<t>.<raw body>", where t is the timestamp from the X-Archive-Signature header and
the raw body is the exact bytes received — verify before parsing the JSON. The HMAC key is the
full secret including the whsec_ prefix.
import { createHmac, timingSafeEqual } from 'node:crypto'
// rawBody: Buffer or string of the exact request body bytes
export function verifyArchiveSignature(rawBody, signatureHeader, secret) {
const pairs = signatureHeader.split(',').map((part) => {
const eq = part.indexOf('=')
return [part.slice(0, eq), part.slice(eq + 1)]
})
const t = pairs.find(([key]) => key === 't')?.[1]
const candidates = pairs.filter(([key]) => key === 'v1').map(([, value]) => value)
if (!t || candidates.length === 0) return false
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
return candidates.some(
(candidate) =>
candidate.length === expected.length &&
timingSafeEqual(Buffer.from(candidate), Buffer.from(expected))
)
}The 5-minute timestamp tolerance is your replay defense — the sender does not enforce it for you.
Use a constant-time comparison, and check every v1= value: after
rotateWebhookSubscriptionSecret, deliveries carry two signatures for 24 hours (new secret first)
so you can roll the new secret out without dropping events. Accept if either matches. Rotation
mints a new secret on every call — don’t retry it blindly.
Retries and automatic disabling
A failed delivery is retried on a backoff ladder: 8 attempts total, spaced roughly
1 minute → 5 minutes → 30 minutes → 2 hours → 6 hours → 12 hours → 24 hours (with jitter), about
two days end to end. A delivery that exhausts the ladder becomes terminally FAILED — visible in
webhookDeliveries with nextAttemptAt: null — and can be
replayed manually with redeliverWebhookDelivery. Transport failures (connection refused,
connect timeout) consume retry attempts like any other failure; once they repeat consecutively,
sends to your endpoint are briefly deferred instead of being fired into a dead host, so a
sustained outage doesn’t burn through the rest of the ladder. Sustained matching bursts are paced
out over time; nothing is dropped.
A subscription is automatically disabled (status: DISABLED_BY_FAILURES) when your endpoint
returns 410 Gone (the clean way to decommission an endpoint — delivery stops immediately),
or after prolonged continuous failure. You’ll see it in the subscription’s status and
disabledAt, as a subscription.disabled event in webhookEvents,
and as an email to workspace admins. Re-arm it once your endpoint is healthy:
mutation EnableWebhookSubscriptionDefault($id: ID!, $replayFailedSince24h: Boolean) {
enableWebhookSubscription(id: $id, replayFailedSince24h: $replayFailedSince24h) {
webhookSubscription {
id
name
status
consecutiveFailures
disabledAt
}
userErrors {
field
message
}
}
} {
"id": "41662e09-10c4-52e4-844a-62ddedb38f11",
"replayFailedSince24h": false
} Authorization: Bearer docs_demo_token_0000000000000000000000
WORKSPACE-ID: 6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a curl https://app.archive.com/api/v2 \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer docs_demo_token_0000000000000000000000' \
-H 'WORKSPACE-ID: 6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a' \
-d '{
"query": "mutation EnableWebhookSubscriptionDefault($id: ID!, $replayFailedSince24h: Boolean) {\n enableWebhookSubscription(id: $id, replayFailedSince24h: $replayFailedSince24h) {\n webhookSubscription {\n id\n name\n status\n consecutiveFailures\n disabledAt\n }\n userErrors {\n field\n message\n }\n }\n}",
"variables": {
"id": "41662e09-10c4-52e4-844a-62ddedb38f11",
"replayFailedSince24h": false
}
}' const response = await fetch("https://app.archive.com/api/v2", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID": "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a"
},
body: JSON.stringify({
query: `mutation EnableWebhookSubscriptionDefault($id: ID!, $replayFailedSince24h: Boolean) {
enableWebhookSubscription(id: $id, replayFailedSince24h: $replayFailedSince24h) {
webhookSubscription {
id
name
status
consecutiveFailures
disabledAt
}
userErrors {
field
message
}
}
}`,
variables: {
"id": "41662e09-10c4-52e4-844a-62ddedb38f11",
"replayFailedSince24h": false
}
})
})
console.log(JSON.stringify(await response.json(), null, 2)) require "json"
require "net/http"
query = <<~'GRAPHQL'
mutation EnableWebhookSubscriptionDefault($id: ID!, $replayFailedSince24h: Boolean) {
enableWebhookSubscription(id: $id, replayFailedSince24h: $replayFailedSince24h) {
webhookSubscription {
id
name
status
consecutiveFailures
disabledAt
}
userErrors {
field
message
}
}
}
GRAPHQL
response = Net::HTTP.post(
URI("https://app.archive.com/api/v2"),
{ query: query, variables: { "id" => "41662e09-10c4-52e4-844a-62ddedb38f11", "replayFailedSince24h" => false } }.to_json,
"Content-Type" => "application/json",
"Authorization" => "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID" => "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a"
)
puts response.body import json
import requests
query = """
mutation EnableWebhookSubscriptionDefault($id: ID!, $replayFailedSince24h: Boolean) {
enableWebhookSubscription(id: $id, replayFailedSince24h: $replayFailedSince24h) {
webhookSubscription {
id
name
status
consecutiveFailures
disabledAt
}
userErrors {
field
message
}
}
}
"""
response = requests.post(
"https://app.archive.com/api/v2",
json={"query": query, "variables": {"id": "41662e09-10c4-52e4-844a-62ddedb38f11", "replayFailedSince24h": False}},
headers={
"Authorization": "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID": "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a",
},
)
print(json.dumps(response.json(), indent=2)) replayFailedSince24h defaults to true and replays the last 24 hours of failed deliveries
one-shot each; pass false to re-enable without the replay. Events that fired while the
subscription was failure-disabled sit in the outbox — page webhookEvents to reconcile anything
older than the replay window.
Test and monitor
sendWebhookTestEvent POSTs a signed synthetic ping through the full delivery pipeline and
returns the settled delivery, so you can verify transport and your signature check end to end
before real traffic arrives. The ping’s data is { ping, subscription_id, sent_at } — not the
item shape — and repeated pings to one subscription are throttled with a short cooldown (the
userError tells you how long to wait).
mutation SendWebhookTestEventDefault($id: ID!) {
sendWebhookTestEvent(id: $id) {
webhookDelivery {
id
subscriptionId
eventId
status
attemptCount
lastResponseStatus
responseTimeMs
}
userErrors {
field
message
}
}
} {
"id": "eb418e28-bf9d-5350-b87b-446a6813bca4"
} Authorization: Bearer docs_demo_token_0000000000000000000000
WORKSPACE-ID: 6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a curl https://app.archive.com/api/v2 \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer docs_demo_token_0000000000000000000000' \
-H 'WORKSPACE-ID: 6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a' \
-d '{
"query": "mutation SendWebhookTestEventDefault($id: ID!) {\n sendWebhookTestEvent(id: $id) {\n webhookDelivery {\n id\n subscriptionId\n eventId\n status\n attemptCount\n lastResponseStatus\n responseTimeMs\n }\n userErrors {\n field\n message\n }\n }\n}",
"variables": {
"id": "eb418e28-bf9d-5350-b87b-446a6813bca4"
}
}' const response = await fetch("https://app.archive.com/api/v2", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID": "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a"
},
body: JSON.stringify({
query: `mutation SendWebhookTestEventDefault($id: ID!) {
sendWebhookTestEvent(id: $id) {
webhookDelivery {
id
subscriptionId
eventId
status
attemptCount
lastResponseStatus
responseTimeMs
}
userErrors {
field
message
}
}
}`,
variables: {
"id": "eb418e28-bf9d-5350-b87b-446a6813bca4"
}
})
})
console.log(JSON.stringify(await response.json(), null, 2)) require "json"
require "net/http"
query = <<~'GRAPHQL'
mutation SendWebhookTestEventDefault($id: ID!) {
sendWebhookTestEvent(id: $id) {
webhookDelivery {
id
subscriptionId
eventId
status
attemptCount
lastResponseStatus
responseTimeMs
}
userErrors {
field
message
}
}
}
GRAPHQL
response = Net::HTTP.post(
URI("https://app.archive.com/api/v2"),
{ query: query, variables: { "id" => "eb418e28-bf9d-5350-b87b-446a6813bca4" } }.to_json,
"Content-Type" => "application/json",
"Authorization" => "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID" => "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a"
)
puts response.body import json
import requests
query = """
mutation SendWebhookTestEventDefault($id: ID!) {
sendWebhookTestEvent(id: $id) {
webhookDelivery {
id
subscriptionId
eventId
status
attemptCount
lastResponseStatus
responseTimeMs
}
userErrors {
field
message
}
}
}
"""
response = requests.post(
"https://app.archive.com/api/v2",
json={"query": query, "variables": {"id": "eb418e28-bf9d-5350-b87b-446a6813bca4"}},
headers={
"Authorization": "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID": "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a",
},
)
print(json.dumps(response.json(), indent=2)) {
"data": {
"sendWebhookTestEvent": {
"webhookDelivery": {
"id": "51e830cd-4071-5889-9304-2ac229f5cf70",
"subscriptionId": "eb418e28-bf9d-5350-b87b-446a6813bca4",
"eventId": "4849ae8e-b058-59bb-95b3-4e993b82807c",
"status": "SUCCEEDED",
"attemptCount": 1,
"lastResponseStatus": 200,
"responseTimeMs": 142
},
"userErrors": []
}
}
} For ongoing monitoring, filter deliveries to FAILED — anything with nextAttemptAt: null has
spent its ladder and is waiting on you:
query WebhookDeliveriesDefault($filter: WebhookDeliveryFilterInput, $first: Int) {
webhookDeliveries(filter: $filter, first: $first) {
nodes {
id
subscriptionId
eventId
status
attemptCount
lastResponseStatus
lastError
responseTimeMs
nextAttemptAt
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
} {
"filter": {
"status": "FAILED"
},
"first": 10
} Authorization: Bearer docs_demo_token_0000000000000000000000
WORKSPACE-ID: 6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a curl https://app.archive.com/api/v2 \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer docs_demo_token_0000000000000000000000' \
-H 'WORKSPACE-ID: 6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a' \
-d '{
"query": "query WebhookDeliveriesDefault($filter: WebhookDeliveryFilterInput, $first: Int) {\n webhookDeliveries(filter: $filter, first: $first) {\n nodes {\n id\n subscriptionId\n eventId\n status\n attemptCount\n lastResponseStatus\n lastError\n responseTimeMs\n nextAttemptAt\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n totalCount\n }\n}",
"variables": {
"filter": {
"status": "FAILED"
},
"first": 10
}
}' const response = await fetch("https://app.archive.com/api/v2", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID": "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a"
},
body: JSON.stringify({
query: `query WebhookDeliveriesDefault($filter: WebhookDeliveryFilterInput, $first: Int) {
webhookDeliveries(filter: $filter, first: $first) {
nodes {
id
subscriptionId
eventId
status
attemptCount
lastResponseStatus
lastError
responseTimeMs
nextAttemptAt
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}`,
variables: {
"filter": {
"status": "FAILED"
},
"first": 10
}
})
})
console.log(JSON.stringify(await response.json(), null, 2)) require "json"
require "net/http"
query = <<~'GRAPHQL'
query WebhookDeliveriesDefault($filter: WebhookDeliveryFilterInput, $first: Int) {
webhookDeliveries(filter: $filter, first: $first) {
nodes {
id
subscriptionId
eventId
status
attemptCount
lastResponseStatus
lastError
responseTimeMs
nextAttemptAt
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
GRAPHQL
response = Net::HTTP.post(
URI("https://app.archive.com/api/v2"),
{ query: query, variables: { "filter" => { "status" => "FAILED" }, "first" => 10 } }.to_json,
"Content-Type" => "application/json",
"Authorization" => "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID" => "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a"
)
puts response.body import json
import requests
query = """
query WebhookDeliveriesDefault($filter: WebhookDeliveryFilterInput, $first: Int) {
webhookDeliveries(filter: $filter, first: $first) {
nodes {
id
subscriptionId
eventId
status
attemptCount
lastResponseStatus
lastError
responseTimeMs
nextAttemptAt
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
"""
response = requests.post(
"https://app.archive.com/api/v2",
json={"query": query, "variables": {"filter": {"status": "FAILED"}, "first": 10}},
headers={
"Authorization": "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID": "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a",
},
)
print(json.dumps(response.json(), indent=2)) {
"data": {
"webhookDeliveries": {
"nodes": [
{
"id": "dbe6be29-b520-5cdc-81a3-b7a0d5d8f62e",
"subscriptionId": "41662e09-10c4-52e4-844a-62ddedb38f11",
"eventId": "0f8e0d63-409e-5fd1-88bd-2febbe05fa53",
"status": "FAILED",
"attemptCount": 8,
"lastResponseStatus": 500,
"lastError": "HTTP 500 from endpoint",
"responseTimeMs": 87,
"nextAttemptAt": null
}
],
"pageInfo": {
"hasNextPage": false,
"endCursor": "Z2lkOi8vYXJjaGl2ZS9XZWJob29rRGVsaXZlcnkvMjAyNC0xMi0zMVQxNTowMDowMC4wMDAwMDBafGRiZTZiZTI5LWI1MjAtNWNkYy04MWEzLWI3YTBkNWQ4ZjYyZQ=="
},
"totalCount": 1
}
}
} webhookEvents is the outbox: every event that ever fired, whether or not its deliveries
succeeded. Page it on cold start (or after downtime) to catch anything you missed — the id is
the same event_id you deduplicate deliveries on.
query WebhookEventsDefault($filter: WebhookEventFilterInput, $first: Int) {
webhookEvents(filter: $filter, first: $first) {
nodes {
id
eventType
eventVersion
payload
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
} {
"filter": {
"eventTypes": [
"content_view.item_added"
]
},
"first": 10
} Authorization: Bearer docs_demo_token_0000000000000000000000
WORKSPACE-ID: 6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a curl https://app.archive.com/api/v2 \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer docs_demo_token_0000000000000000000000' \
-H 'WORKSPACE-ID: 6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a' \
-d '{
"query": "query WebhookEventsDefault($filter: WebhookEventFilterInput, $first: Int) {\n webhookEvents(filter: $filter, first: $first) {\n nodes {\n id\n eventType\n eventVersion\n payload\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n totalCount\n }\n}",
"variables": {
"filter": {
"eventTypes": [
"content_view.item_added"
]
},
"first": 10
}
}' const response = await fetch("https://app.archive.com/api/v2", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID": "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a"
},
body: JSON.stringify({
query: `query WebhookEventsDefault($filter: WebhookEventFilterInput, $first: Int) {
webhookEvents(filter: $filter, first: $first) {
nodes {
id
eventType
eventVersion
payload
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}`,
variables: {
"filter": {
"eventTypes": [
"content_view.item_added"
]
},
"first": 10
}
})
})
console.log(JSON.stringify(await response.json(), null, 2)) require "json"
require "net/http"
query = <<~'GRAPHQL'
query WebhookEventsDefault($filter: WebhookEventFilterInput, $first: Int) {
webhookEvents(filter: $filter, first: $first) {
nodes {
id
eventType
eventVersion
payload
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
GRAPHQL
response = Net::HTTP.post(
URI("https://app.archive.com/api/v2"),
{ query: query, variables: { "filter" => { "eventTypes" => ["content_view.item_added"] }, "first" => 10 } }.to_json,
"Content-Type" => "application/json",
"Authorization" => "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID" => "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a"
)
puts response.body import json
import requests
query = """
query WebhookEventsDefault($filter: WebhookEventFilterInput, $first: Int) {
webhookEvents(filter: $filter, first: $first) {
nodes {
id
eventType
eventVersion
payload
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
"""
response = requests.post(
"https://app.archive.com/api/v2",
json={"query": query, "variables": {"filter": {"eventTypes": ["content_view.item_added"]}, "first": 10}},
headers={
"Authorization": "Bearer docs_demo_token_0000000000000000000000",
"WORKSPACE-ID": "6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a",
},
)
print(json.dumps(response.json(), indent=2)) {
"data": {
"webhookEvents": {
"nodes": [
{
"id": "0f8e0d63-409e-5fd1-88bd-2febbe05fa53",
"eventType": "content_view.item_added",
"eventVersion": "1.0",
"payload": {
"item": {
"id": "12dedc45-e264-5d57-91f7-4d9aa7a05849"
},
"view": {
"id": "eaa824b8-374f-5db8-bee2-51dfd8c3776c"
}
}
},
{
"id": "7f412c82-762e-5978-b0ae-bdcac334b9b5",
"eventType": "content_view.item_added",
"eventVersion": "1.0",
"payload": {
"item": {
"id": "12dedc45-e264-5d57-91f7-4d9aa7a05849"
},
"view": {
"id": "eaa824b8-374f-5db8-bee2-51dfd8c3776c"
}
}
}
],
"pageInfo": {
"hasNextPage": false,
"endCursor": "Z2lkOi8vYXJjaGl2ZS9XZWJob29rRXZlbnQvMjAyNC0xMi0zMVQxNDowMDowMC4wMDAwMDBafDdmNDEyYzgyLTc2MmUtNTk3OC1iMGFlLWJkY2FjMzM0YjliNQ=="
},
"totalCount": 2
}
}
} Limits
| Plan | Subscriptions per workspace | Views per subscription |
|---|---|---|
| Trial | 1 | 1 |
| Startup | 2 | 5 |
| Growth / Agency | 5 | 10 |
| Enterprise | 10 | 25 |
metadata is capped at 4 KB on every plan. There is no cap on delivery volume — bursts are paced,
not dropped. If you need more subscriptions or views per subscription,
contact support: both caps can be raised per workspace.
Where to go next
createWebhookSubscriptionreference — full input and payload shapes, plus the other six webhook mutations alongside it.webhookDeliveriesandwebhookEvents— the monitoring surface.webhookSubscriptionsand its paginated twinwebhookSubscriptionsConnection— list what’s subscribed; AI agents reach the same list through thegetWebhookSubscriptionsMCP tool.- Filtering & search — building the views that act as your event filters.