Local assignment webhooks
Assign a local task to an agent profile and notify your existing runner over HTTP. Alshitask sends the event; your receiver decides how to start Factory, Codex, Claude Code, or another tool. Agents read and update tasks through their existing MCP connection.
Availability: builds containing commit 36f2bad, currently published on feat/local-webhooks. Older packaged desktop releases may not show this setting. This feature supports local boards in desktop and server mode; connected portal boards are excluded.
Open the local workspace's settings (gear icon) → Webhooks. Add a receiving URL,
optional board/assignee filters and events. New subscriptions start disabled.
Copy the generated signing secret into your receiver, enable the subscription,
and use Send test event. A test has type webhook.test: acknowledge it without
starting work. Saving configuration changes cancels pending deliveries from the
previous configuration. Rotate secret shows a replacement once and requires
updating the receiver. Disable pauses new notifications and cancels queued ones;
a request already in flight may still arrive.
The destination must be loopback or a private network. HTTP and HTTPS are supported; HTTPS certificates are verified normally. Public, link-local/metadata and mixed private/public DNS destinations are rejected. The worker resolves and validates all addresses on every attempt, pins the selected address for connection, and never follows redirects. In Docker, localhost means the container: use the receiver's private bridge address, not the host's loopback address.
Event contract v1
POST with Content-Type: application/json:
{
"schema_version": 1,
"id": "unique-event-uuid",
"type": "task.assignment.changed",
"occurred_at": "2026-10-15T20:00:00+00:00",
"source": {"instance_id": "installation-uuid", "workspace_id": "local-workspace-uuid"},
"task": {"id": "task-uuid", "board_id": "board-uuid-or-null", "revision": 4},
"previous_assignee": null,
"assignee": {"id": "profile:profile-uuid", "kind": "droid"}
}
Events are task.assignment.changed and task.deleted. Assigned task creation
counts as assignment; unchanged assignments, comment/status edits and restores do
not trigger work. Assignee filters match either old or new identity, including
unassignment. Delivery is for local tasks only; portal sync/import is excluded.
The test envelope contains the version, ID, type, occurrence time and source only.
Task content, credentials, commands and provider selections are not in the event.
Headers:
X-AlshiTasks-Event-Id: matches the event ID.X-AlshiTasks-Timestamp: Unix seconds for this delivery attempt.X-AlshiTasks-Signature:sha256=followed by the lowercase hex HMAC-SHA256 oftimestamp + "." + exact_raw_request_body, using the signing secret.
Verify the signature in constant time and reject timestamps more than five
minutes from your clock. Deduplicate durably by (source.instance_id, id);
reject a reused ID with different contents. Acknowledge only after durable
acceptance; any 2xx means accepted, not completed. Keep requests short and run
work asynchronously. See the minimal Python receiver for a receiver without
any agent framework. Its intentionally minimal in-memory behavior is not a
production work queue.
Delivery and cancellation
Task changes and deliveries commit together. The supervised worker performs network I/O outside the database transaction with a 10-second socket timeout. Delivery is at least once. Transient connection failures, HTTP 408/425/429 and 5xx retry at 5 seconds, doubling to one hour, for up to 24 hours. Other HTTP failures require correction and manual retry. Manual retry keeps the event ID. Configuration changes invalidate queued attempts. History is retained 30 days.
Before launching work, receivers can use their existing project-read credential
with GET /v1/projects/items/{id}/assignment?workspace_id={workspace}. It returns
instance/workspace/task/board IDs, current revision, assignment_revision,
assignee ID/active state, deletion state and task number. assignment_revision
changes on assignment and delete/restore, not on ordinary edits, allowing a
receiver to reject delayed A→B→A events without rejecting later comments.
This additive REST endpoint does not change the shared MCP tool contract.
Agents continue to read and update tasks using the existing MCP tools.
Reassignment/deletion is a notification, not an execution mechanism. The receiver owns cancellation and must wait for the previous execution to stop before starting another for that source task. Completed side effects are not rolled back. AlshiTasks has no dependency on Factory, Codex, Claude Code, or a run dashboard.
Storage and recovery
Only the local owner may manage subscriptions. Signing secrets use the existing OS credential store or encrypted server vault; they never sync to the portal or appear in delivery logs. Subscription settings and event IDs are installation local. A workspace restore clears subscriptions and pending deliveries, creates a fresh source identity, and requires reconfiguration. Existing files and task history are preserved by the usual backup/restore process. Use a complete private instance backup with its separate vault key for operator recovery.
Try a receiver locally
- In Settings → Device settings → Manage people and droids, create a Droid profile if needed. A profile is an assignee identity; it does not start a process by itself.
- In the local workspace settings, add a webhook for your receiver. Use its private URL and select the Droid assignee. Keep both assignment-change and deletion events selected so the receiver can cancel old work. Save it disabled and copy the signing secret.
- From a checkout containing the webhook feature, start the demonstration receiver in Bash:
read -r -s -p 'Signing secret: ' WEBHOOK_SECRET
export WEBHOOK_SECRET
python3 scripts/webhook-example.py
The example listens at http://127.0.0.1:43122/. Keep that terminal running, enable the webhook, and select Send test event. Delivery history should show delivered with HTTP 202. The terminal prints only event type and ID. This example does not launch an agent or durably deduplicate work; use it to verify connectivity and signing.
- For real work, replace the example with a receiver that durably accepts events, verifies current assignment, and submits work to your existing agent harness. Keep the signing secret and MCP bearer credential separate.
- Assign a disposable task to the selected Droid. Watch delivery history and the receiver's run view, then unassign it to test cancellation. Enabling a webhook does not process existing assignments.
A Docker sender cannot reach a host receiver through its own 127.0.0.1. Bind the receiver to the actual private bridge or another reachable private address and use that address in the webhook URL. Do not assume every host uses the same bridge address.
Connecting an existing harness
The receiver contract is provider independent: verify → persist/deduplicate → read current assignment → queue an existing execution. Retain source instance, workspace, task, assignment revision and event ID on the run. Acknowledge quickly; stream execution progress through your existing run UI.
The Eyefinity receiver example connects these events to an existing supervisor and Factory console Runs page. Its bindings select existing Factory or Claude execution paths. Codex and other tools can consume the same events through their own receivers; Alshitask does not bundle their runners.
Keep a receipt after run-history pruning, reject reused event IDs with different contents, and recheck assignment at queue admission. When reassigned or deleted, stop the prior execution and wait for confirmed termination before launching a replacement. Preserve completed work. The reference receiver keeps active work alive during a temporary source outage and waits to start new work until it can verify assignment.
Troubleshooting
| Symptom | Check |
|---|---|
| No delivery after assignment | Webhook enabled, correct local board and assignee filters, and an actual assignment change after enabling. |
| Connection failed | Receiver running; correct host/container address and port; private DNS resolution and routing. |
| HTTP 401 | Matching signing secret, exact raw body, signature header, and synchronized clocks. |
| HTTP 4xx or failed | Correct receiver path/schema/source identity; fix the cause before Retry delivery. |
| Delivered but no run | Delivered means accepted. Check receiver receipts, assignment freshness, assignee binding, task deletion and runner availability. |
| Repeated deliveries | Expected after uncertain acknowledgment; deduplicate by source instance plus event ID. |
| Assignment changes do not cancel | Receiver must implement cancellation; select assignment-change and deletion events and match either old or new assignee. |
| No Webhooks section | Select a local workspace, use its owner session, and confirm the installed build contains the feature. |
Management routes are owner-only: GET/POST /v1/webhooks, PATCH/DELETE /v1/webhooks/{id}, GET .../{id}/deliveries, POST .../{id}/test, and POST .../{id}/deliveries/{delivery_id}/retry. Updates require the observed generation; ordinary project bearer credentials cannot manage subscriptions. Read task state through the scoped assignment endpoint above. See Desktop API and Agents and MCP for the existing access model.