Skip to content

Subscriptions & Actions

Subscription endpoints manage event-driven actions — creating, pausing, and deleting subscriptions. Action endpoints provide observability into delivery runs, attempts, and terminal-failure notifications.

All paths are prefixed with /api/repos/:orgName/:repoName.

For conceptual background, see Subscriptions. For CLI and MCP interfaces, see Creating Subscriptions and Managing Subscriptions.


List all subscriptions in a repository.

Auth: None (public repos). Private repos require authentication.

[
{
"name": "signal-hook",
"kind": "webhook",
"active": true,
"shapeName": "Signal",
"webhookUrl": "https://example.com/hook"
},
{
"name": "daily-sync",
"kind": "cron",
"active": true,
"executorKind": "sprite",
"cronConfig": { "cronspec": "0 8 * * *", "timezone": "America/New_York" }
},
{
"name": "cross-repo-hook",
"kind": "webhook",
"active": true,
"shapeName": "Signal",
"webhookUrl": "https://example.com/hook",
"sourceRepo": "myorg/other-repo"
}
]

Cross-repo subscriptions include a sourceRepo string identifying the repository being watched (formatted as org/repo), when the caller has read access to the source repository. Subscriptions without sourceRepo either watch their own repository or the caller lacks access to the source repository metadata.

Terminal window
curl "https://api.warmhub.ai/api/repos/myorg/myrepo/subs"

Get a single subscription by name.

Auth: None (public repos). Private repos require authentication.

ParameterTypeDescription
namestringSubscription name (URL-encoded)
{
"name": "signal-hook",
"kind": "webhook",
"active": true,
"shapeName": "Signal",
"webhookUrl": "https://example.com/hook",
"executorKind": "webhook",
"createdAt": 1741132800000
}

For subscriptions executed by sprite (including cron subscriptions with executorKind: "sprite"), the response also includes actionContainer (the derived container name), actionContainerConfig (the raw container configuration with command, githubRepo, ref, inputMode), and tokenScopes when sprite-minted WarmHub tokens are scoped:

{
"name": "sprite-hook",
"kind": "sprite",
"active": true,
"shapeName": "Signal",
"executorKind": "sprite",
"actionContainer": "wh-warmdex-action-test",
"actionContainerConfig": {
"command": "bun run action",
"githubRepo": "warmdex/action-test",
"ref": "main",
"inputMode": "stdin"
},
"tokenScopes": [
{
"permissions": ["repo:read"],
"allowedMatches": ["Signal/*"]
}
],
"createdAt": 1741132800000
}

Cross-repo subscriptions include a sourceRepo string identifying the repository being watched (formatted as org/repo), when the caller has read access to the source repository:

{
"name": "cross-repo-hook",
"kind": "webhook",
"active": true,
"shapeName": "Signal",
"webhookUrl": "https://example.com/hook",
"executorKind": "webhook",
"sourceRepo": "myorg/other-repo",
"createdAt": 1741132800000
}
CodeStatusDescription
NOT_FOUND404Subscription not found
Terminal window
curl "https://api.warmhub.ai/api/repos/myorg/myrepo/subs/signal-hook"

Create a new subscription. The request body varies by kind — see Creating Subscriptions for full configuration details.

Auth: Required, repo:write scope.

FieldTypeRequiredDescription
namestringYesSubscription name
kindstringYes"webhook"
shapeNamestringYesShape to subscribe to (also accepts on)
filterJsonobjectYesFilter for matching operations (also accepts filter)
webhookUrlstringYesDestination URL (also accepts url)
FieldTypeRequiredDescription
namestringYesSubscription name
kindstringYes"sprite"
shapeNamestringYesShape to subscribe to
filterJsonobjectYesFilter for matching operations
spriteConfigobjectYesSprite execution configuration
tokenScopesarrayNoOptional scoped WarmHub token permissions for sprite runs. Must be a non-empty array when provided

The spriteConfig object:

FieldTypeRequiredDescription
commandstringYesShell command to execute
inputModestringYes"stdin", "json-file", or "env"
githubRepostringNoGitHub repo to clone (owner/repo or "none")
refstringIf cloningGit ref to checkout
spriteNamestringNoCustom execution environment name
inputPathstringNoFile path for json-file input mode
spritePoolstringNoRepo-scoped execution pool name
tokenTtlMinutesnumberNoWarmHub token TTL in minutes (default 10, range 5–60)
FieldTypeRequiredDescription
namestringYesSubscription name
kindstringYes"cron"
executorKindstringYes"webhook" or "sprite" (also accepts executor)
cronConfigobjectYesCron schedule configuration
webhookUrlstringIf webhookDestination URL
spriteConfigobjectIf spriteSprite execution configuration
tokenScopesarrayNoOptional scoped WarmHub token permissions for sprite runs. Must be a non-empty array when provided

The cronConfig object:

FieldTypeRequiredDescription
cronspecstringYesCron expression (5-field format, minimum 5-minute interval)
timezonestringNoIANA timezone (default UTC)

Returns the created subscription object.

When tokenScopes is provided:

  • it must contain at least one entry
  • each entry must include a non-empty permissions array
  • a permission may appear in only one entry across the full array
  • allowedMatches, when provided, must be an array of valid non-empty glob strings
CodeStatusDescription
ALREADY_EXISTS409A subscription with this name already exists
VALIDATION_ERROR400Invalid configuration, missing required fields, or invalid cronspec
NOT_FOUND404Shape not found
RATE_LIMITED429Too many subscription creation requests. Honor Retry-After before retrying.
Terminal window
# Create a webhook subscription
curl -X POST https://api.warmhub.ai/api/repos/myorg/myrepo/subs \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "signal-hook",
"kind": "webhook",
"shapeName": "Signal",
"filterJson": { "shape": "Signal" },
"webhookUrl": "https://example.com/hook"
}'
Terminal window
# Create a sprite subscription with scoped WarmHub token permissions
curl -X POST https://api.warmhub.ai/api/repos/myorg/myrepo/subs \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "signal-sprite",
"kind": "sprite",
"shapeName": "Signal",
"filterJson": { "shape": "Signal" },
"spriteConfig": {
"command": "bun run action",
"inputMode": "stdin"
},
"tokenScopes": [
{
"permissions": ["repo:read"],
"allowedMatches": ["Signal/*"]
}
]
}'

Update an existing subscription. Provide only the fields you want to change. For nested spriteConfig and cronConfig, provided keys are merged into the existing config.

Auth: Required, repo:write scope.

ParameterTypeDescription
namestringSubscription name (URL-encoded)
FieldTypeRequiredDescription
shapeNamestringNoReplacement shape name (also accepts on)
filterJsonobjectNoReplacement filter object (also accepts filter)
webhookUrlstringNoReplacement webhook URL (also accepts url)
spriteConfigobjectNoSprite config patch
cronConfigobjectNoCron config patch
executorKindstringNoReplacement cron executor kind (also accepts executor)
allowTraceReentrybooleanNoReplacement same-trace reentry policy
workspacePolicystringNoReplacement workspace commit policy: "ignore" or "include"

Returns the updated subscription object.

Terminal window
curl -X PATCH https://api.warmhub.ai/api/repos/myorg/myrepo/subs/signal-hook \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://example.com/new-hook",
"workspacePolicy": "include"
}'

Pause a subscription. No new deliveries are created while paused. For cron subscriptions, the cron job is deregistered.

Auth: Required, repo:write scope.

ParameterTypeDescription
namestringSubscription name (URL-encoded)
{
"ok": true
}
Terminal window
curl -X POST https://api.warmhub.ai/api/repos/myorg/myrepo/subs/signal-hook/pause \
-H "Authorization: Bearer <token>"

Resume a paused subscription.

Auth: Required, repo:write scope.

ParameterTypeDescription
namestringSubscription name (URL-encoded)
{
"ok": true
}
Terminal window
curl -X POST https://api.warmhub.ai/api/repos/myorg/myrepo/subs/signal-hook/resume \
-H "Authorization: Bearer <token>"

Bind a credential set to a subscription for authenticated webhook delivery.

Auth: Required, repo:write scope.

ParameterTypeDescription
namestringSubscription name (URL-encoded)
FieldTypeRequiredDescription
credentialSetNamestringYesName of the credential set to bind
{
"bound": true,
"subscriptionName": "signal-hook",
"credentialSetName": "webhook-keys"
}
Terminal window
curl -X POST https://api.warmhub.ai/api/repos/myorg/myrepo/subs/signal-hook/bind \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"credentialSetName": "webhook-keys"}'

Unbind a credential set from a subscription.

Auth: Required, repo:write scope.

ParameterTypeDescription
namestringSubscription name (URL-encoded)
{
"unbound": true,
"subscriptionName": "signal-hook"
}
Terminal window
curl -X DELETE https://api.warmhub.ai/api/repos/myorg/myrepo/subs/signal-hook/bind \
-H "Authorization: Bearer <token>"

Delete a subscription. Removes the subscription, its credential bindings, and any associated cron schedule.

Auth: Required, repo:write scope.

ParameterTypeDescription
namestringSubscription name (URL-encoded)
{
"ok": true
}
Terminal window
curl -X DELETE https://api.warmhub.ai/api/repos/myorg/myrepo/subs/signal-hook \
-H "Authorization: Bearer <token>"

Action endpoints provide observability into subscription delivery history. See Managing Subscriptions for the full observability surface including CLI and MCP access.

List action runs for a repository, optionally filtered by status.

Auth: None (public repos). Private repos require authentication.

ParameterTypeRequiredDescription
statusstringNoFilter by status: pending, running, retry_wait, succeeded, failed_terminal, dead_letter
sinceintegerNoRuns created after this timestamp (epoch ms)
limitintegerNoMaximum runs to return
[
{
"subscriptionName": "signal-hook",
"commitId": "a1b2c3d4e5f6a7b8",
"status": "succeeded",
"executorKind": "webhook",
"matchedOperationIndexes": [0, 1],
"attemptCount": 1,
"maxAttempts": 5,
"createdAt": 1741132800000,
"updatedAt": 1741132801000
}
]

Failed runs also include lastErrorCode and lastErrorMessage fields.

Terminal window
# All failed runs
curl "https://api.warmhub.ai/api/repos/myorg/myrepo/actions/runs?status=failed_terminal"
# Recent runs
curl "https://api.warmhub.ai/api/repos/myorg/myrepo/actions/runs?limit=20"

GET /actions/subs/:subName/commits/:commitId/attempts

Section titled “GET /actions/subs/:subName/commits/:commitId/attempts”

Get the attempt history for a specific action run.

Auth: None (public repos). Private repos require authentication.

ParameterTypeDescription
subNamestringSubscription name (URL-encoded)
commitIdstringCommit identifier (16-char hex string)
[
{
"attempt": 1,
"status": "failed",
"executorKind": "webhook",
"startedAt": 1741132800000,
"finishedAt": 1741132801000,
"httpStatus": 503,
"errorCode": "WEBHOOK_NETWORK_ERROR",
"errorMessage": "Connection timed out"
},
{
"attempt": 2,
"status": "succeeded",
"executorKind": "webhook",
"startedAt": 1741132802000,
"finishedAt": 1741132803000,
"httpStatus": 200
}
]
CodeStatusDescription
VALIDATION_ERROR400Invalid commit identifier
Terminal window
curl "https://api.warmhub.ai/api/repos/myorg/myrepo/actions/subs/signal-hook/commits/a1b2c3d4e5f6a7b8/attempts"

List repo-scoped action failure notification records for a repository.

Auth: None (public repos). Private repos require authentication.

ParameterTypeRequiredDescription
sinceintegerNoNotifications after this timestamp (epoch ms)
limitintegerNoMaximum notifications to return
[
{
"subscriptionName": "signal-hook",
"commitId": "a1b2c3d4e5f6a7b8",
"attempt": 2,
"channel": "inbox",
"status": "queued",
"errorCode": "WEBHOOK_NETWORK_ERROR",
"errorMessage": "Connection timed out",
"createdAt": 1741132810000
}
]

status here is the notification dispatch state for the channel above (queued or delivered), not the action run outcome. This record exists because the action run already ended in a terminal failure state such as failed_terminal or dead_letter.

Terminal window
curl "https://api.warmhub.ai/api/repos/myorg/myrepo/actions/notifications?limit=10"