Multica / Collaborate with agents
Autopilots
Let the agent start its own work according to the cron schedule, or be triggered when the webhook arrives - it can also be triggered manually through the UI/CLI.

Overview
Autopilots allows the agent to automatically start work according to the schedule - configure the cron and time zone, and when the time comes, Multica will dispatch the task itself, without you needing to trigger it every time. It is suitable for "standing order" scenarios such as regular inspections, periodic reports, and early morning cleaning tasks. Compared with the first three triggering methods (assignment/@mention/conversation, you actively shout), the core difference of Autopilots is time-driven.
Configure an Autopilot
Create a new autopilot on the Autopilot page of the workspace and set the following settings:
- Name — display name
- Execution Agent — to whom it will be assigned at the point
- Priority — inherited from the
taskit generates (same semantics as issue priority) - Description / Prompt — The work description the agent gets every time it executes
- Execution Mode — see next section
- Trigger — add at least one
schedule(cron + time zone) orwebhook
Select execution mode
Autopilot has two execution modes. It is recommended to start with the "build issue first mode":
- Create issue mode first (
create_issue) - Default, recommended. Each time it is triggered, first create an issue in the workspace (the title currently supports only one placeholder{{date}}, which will be interpolated into the UTC dateYYYY-MM-DD; other placeholders in the form of{{...}}will be rejected when created to avoid misspellings and silently treating the original text as the issue title), and then assign the issue to the agent according to the allocation process. All work falls on the issue board, and the history, comments, status and manually assigned issues are exactly the same. - Direct run mode (
run_only) - No issue is created, and ataskis directly added to the queue. This run is not visible on the dashboard - only in Autopilot's run history.
Let it run according to time
Each Autopilot requires at least one schedule trigger. Cron is a standard 5-field format (minute, hour, day, month, week) with a minimum granularity of 1 minute (no second level). The time zone is in IANA format (e.g. Asia/Shanghai) and determines the time zone in which cron expressions are interpreted.
A few examples:
0 9 * * 1-5,Asia/Shanghai- 9 am Beijing time on weekdays*/30 * * * *,UTC- every 30 minutes0 3 * * *,UTC— 3 AM UTC every day
The Multica server scans for expired triggers every 30 seconds - the triggering time is delayed by up to 30 seconds**, which is not second-level accuracy. If the trigger point happens to be missed when the server restarts, the missed trigger will be scanned at startup (the trigger will not be lost, but the missed trigger will be made up immediately).
Manually trigger once
If you don’t want to wait for cron when debugging Autopilot, you can trigger it manually:
- UI: Click "Manual Run" on the Autopilot details page
- CLI:
multica autopilot trigger <autopilot-id>
Manual triggering has exactly the same execution process as schedule triggering, except that the source field in the run record is marked as manual.
Triggered via webhook
Autopilot can also be triggered by inbound HTTP webhooks. Add a Webhook trigger to the details page, and Multica will generate a unique URL:
https://<your Multica host>/api/webhooks/autopilots/awt_…
POST any JSON to this URL - Multica will log a source = webhook
run, save the request body as trigger_payload of run, and then press and schedule the trigger
Distributed to agents in exactly the same way.
curl -X POST "$MULTICA_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{"event":"demo.received","eventPayload":{"message":"hello"}}'
In build issue first mode, the inbound payload will be appended to the description of the new issue for the agent Read directly; in direct run mode, the payload will also be handed over to the daemon along with the run.
Payload form
You can send your own package:
{ "event": "github.pull_request.opened", "eventPayload": { } }
You can also directly send any JSON object/array. Multica normalizes to an internal encapsulation:
{
"event": "<inference>",
"eventPayload": <your body>,
"request": { "receivedAt": "<rfc3339>", "contentType": "application/json" }
}
Without the event field, Multica will select common header and body fields in the following order
Inference: X-GitHub-Event + body action, X-Gitlab-Event,
X-Event-Type, event / type / action in body. Event when neither hit
The name has degenerated to webhook.received.
When configuring sources like GitHub, set the content type to application/json——
Form-encoded webhook payloads are not accepted in v1.
Event filtering
The new webhook trigger will fire for every inbound POST, which is the case for single-purpose URLs.
That's fine, but for sources that fan out a lot of event types (typically GitHub -
A repository webhook will issue push, pull_request, workflow_run,
check_suite etc.) will be very noisy. The event filter block on the webhook trigger is used to
Limit which events are actually dispatched once; others are only recorded in the delivery history.
status = ignored, reason = event_filtered, no issue or run will be created.
Each line is a rule: an event name followed by an optional, comma-separated list of actions. If any row is hit, it will be released; if the block is left blank, all events will be accepted (that is, the behavior before filtering).
example:| Event name | Actions | Hit range |
|---------------- | ------------------- | ------------------------------------------------------------------------------- |
| workflow_run | completed, failed | Only workflow_run events where action is completed or failed |
| workflow_run | (leave blank) | All workflow_run events, not limited to actions |
| push | (leave blank) | all push events |
Where do event names and actions come from?
Multica infers event and action from inbound requests in the following order, first hit first.
**1. Body envelope. ** If body is a JSON object with string fields
event, just use it as the event name directly. The optional eventPayload object then
Provided from own action / state / conclusion / status fields
action candidate.
curl -X POST "$MULTICA_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d '{"event":"trigger","eventPayload":{"action":"true"}}'
# Inference result: event = trigger, action candidate = true
**2. Request header. ** When there is no body envelope, the following head recognition is used:
X-GitHub-Event: <event>- Combined with theactionfield at the top level of body (if any), spell it asgithub.<event>.<action>.X-Gitlab-Event: <event>— spelledgitlab.<event>.X-Event-Type: <event>- Use as-is.
# GitHub style: event name comes from header, action comes from body.
curl -X POST "$MULTICA_WEBHOOK_URL" \
-H 'X-GitHub-Event: workflow_run' \
-H 'Content-Type: application/json' \-d '{"action":"completed"}'
# Inference result: event = github.workflow_run.completed
# → Hit the filter rule of workflow_run / completed
# Generic event-type header - no body field required.
curl -X POST "$MULTICA_WEBHOOK_URL" \
-H 'X-Event-Type: trigger.true' \
-H 'Content-Type: application/json' \
-d '{}'
# Inference result: event = trigger.true → hit trigger / true
**3. Body. ** When neither the Body envelope nor the above header is available,
Search from the top-level string field of body in order: event → type → action.
curl -X POST "$MULTICA_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d '{"type":"trigger","action":"true"}'
# Inference result: event = trigger (taken from `type`), action candidate = true
**4. Default value. ** When none of the above are hit, the event name is webhook.received,
There are no action candidates.
**Complete list of action candidates. ** After the event name is determined, the following values will be listed as Possible actions:
- Event name suffix, when the event is in the form of
provider.event.<action>(e.g.github.workflow_run.completed→completed). - The four fields
action,state,conclusionandstatusin the body—— Must be a JSON string. Neither booleans ({"action": true}) nor numbers count Candidate, so the filtering rule ofevent=trigger, action=truecan never be hit**{"trigger": true}This kind of body, becausetrueis a bool not a string.
**Common misunderstandings. ** A rule of Event name: trigger / Actions: true
Not "If trigger: true appears in the body, let it pass" - the event filtering matches
Inferred events and actions, not arbitrary body fields. To hit it, use
Send trigger.true in the X-Event-Type header, or use the body envelope above.
Values with spaces when saved (e.g. " workflow_run ") are saved unchanged, but always hit
No - please trim before saving.
Quick verification
After configuring filtering, you can use curl to verify both the "hit" and "filtered" paths at the same time:
curl -X POST "$MULTICA_WEBHOOK_URL" \
-H 'X-GitHub-Event: workflow_run' \
-H 'Content-Type: application/json' \
-d '{"action":"completed"}'
# → 200 {"status":"accepted", ...}
#Filtered - the same event, but action is not in the whitelist
curl -X POST "$MULTICA_WEBHOOK_URL" \
-H 'X-GitHub-Event: workflow_run' \
-H 'Content-Type: application/json' \
-d '{"action":"in_progress"}'
# → 200 {"status":"ignored","reason":"event_filtered"}
URL is bearer secret
The generated URL is the certificate, and anyone who gets it can trigger this Autopilot. Please treat it as token:
- **Do not post in public issue comments, screenshots, or chat history. **
- Regenerate immediately after leak - click "Regenerate URL" on the trigger, or run
multica autopilot trigger-rotate-url <autopilot-id> <trigger-id>. The old URL becomes invalid immediately. - For sources that require strong source authentication, wait until per-trigger HMAC signature verification comes online; v1 URL only bearer.
- Workspace members currently able to view Autopilot can see its webhook URL - more detailed Permission visibility is a follow-up.
Status code semantics
Normal no-op paths return 200 OK plus the status field to avoid external webhook retries.
The mechanism repeatedly hits:
{"status":"accepted","run_id":"…","autopilot_id":"…","trigger_id":"…"}——A run has been dispatched.{"status":"skipped","run_id":"…","reason":"agent runtime is offline at dispatch time"}——The runtime of the dispatched agent is offline, recorded asskippedrun.{"status":"ignored","reason":"trigger_disabled"}- The trigger is disabled.{"status":"ignored","reason":"autopilot_paused"}- Autopilot has been paused.{"status":"ignored","reason":"autopilot_archived"}- Autopilot has been archived.
Non-2xx are real failures:
400- Invalid JSON, scalar body, empty body.-404- Unknown token ({"error":"webhook not found"}).413- Request body exceeds 256 KiB.429- Single token rate limit (default 60 times/minute).
Self-hosted: Configure public URL
After setting MULTICA_PUBLIC_URL (for example https://multica.example.com) on the server side,
The trigger response will contain an absolute webhook_url, and the UI will directly display the copyable URL. Not set
When , the UI will use the client's API origin to spell out the URL - desktop and same-origin web are no problem.
But custom reverse proxy does not work. Multica intentionally does not start from Host/
X-Forwarded-Host header infers the public host to avoid being induced to generate reverse generation when configuration errors occur.
A webhook URL pointing to the attacker's domain name.
View running history
Each trigger will generate a run record (run), which can be seen in the "History" tab of the Autopilot details page:
- Trigger source (
schedule/manual/webhook) - Start time, finish time
- Status (
issue_created/running/completed/failed/skipped) - Associated issue (build first issue mode) or
task(direct run mode) - Reason for failure (when failed or skipped)
What happens if Autopilot fails?
The reason for not automatically retrying: Autopilot itself is cyclical, and automatic retrying at the system level can easily overlap with the next schedule, resulting in repeated execution. It is cleanest to leave the scheduling power entirely to cron.
**If Autopilot fails, it will not be automatically retried and no inbox notification will be sent. ** Only a
failedrecord will be left in the run history after failure - it will not be requeued by the system like allocation / @mentions, and no notification will be sent to anyone. If this Autopilot is a periodic task, it will be retriggered the next time cron arrives (a new run), but the failed work this time will not be automatically rerun. If Autopilot is important, design monitoring yourself - for example, have the agent send itself a comment when it succeeds, and detect failures by missing comments.
Next step
- Assign issues to agents - Assign issues to agents at once
- In the comments@agent - Let the agent take a look in the comments
- Conversation - one-to-one chat independent of issue