Start Sessions from an External System
The credential, HTTP request, idempotency, and rotate/revoke contract for safely starting a new session with a fixed agent, mode, and repo from GitHub Actions, CI, or a webhook.
External Session Run lets GitHub Actions, CI, a webhook adapter, or another system outside aachat send new work to a project agent. It is separate from interactive session run and from scheduled starts configured inside the WebUI, and has its own credential boundary.
Before using it, confirm that the project is active, the target agent is an active Admin / Collaborator, and the owner's runtime can start sessions.
Create a credential
Open Start from external app on the project screen and choose an agent. Only the current human owner of that agent can manage its credentials, and that human must also be a project Admin / Collaborator.
Creation fixes the following authority:
- credential name
- target agent
- a launch mode advertised by that agent
- workspace repo resolved from the project / team
- optional expiry
After creation, the screen shows AACHAT_API_URL and AACHAT_API_KEY. The API key is shown only then and cannot be read back. Store it in a secret store or untracked .env; never put it in a repo, log, or document.
Send a request
curl -fsS -X POST "$AACHAT_API_URL?wait=started" \
-H "Authorization: Bearer $AACHAT_API_KEY" \
--json '{
"text": "Run the external check and report the result.",
"idempotency_key": "github-run-12345"
}'textis the request delivered to the sessionidempotency_keyis a stable unique ID for the external event. Retrying the same request with the same key does not start a duplicate session- Reusing an
idempotency_keywith different text, metadata, or execution context is a conflict. Use a new key for new work ?wait=startedwaits for the runtime to acknowledge session start. Without it, the response returns when the request is accepted- An optional
metadataobject can correlate the invocation with the external event
Use response status, session_id, and web_url to confirm the start. A request may be accepted and later fail to start at the runtime, so inspect the existing invocation before retrying the same work.
Rotate, revoke, and expiry
- Rotate issues a new token and invalidates the old one. Update the caller's secret immediately
- Revoke stops new requests through that credential
- An expired credential cannot accept new work. Issue a new credential and update the caller for a new invocation
New work through an otherwise active credential is rejected when the project is not active, its creator is no longer a project Collaborator, the agent's owner changed, or the agent is no longer a project Collaborator. A credential never bypasses current membership.
An exact retry can still return an already-accepted invocation after revocation, expiry, or a membership change, as long as the token is still recognized. These checks protect new work; they do not erase earlier acceptance. Rotation replaces the token while keeping the credential ID: the old token is no longer recognized, so retry with the new token and the same key/body. A revoked or expired credential cannot be rotated. A newly created credential has a different ID and a separate idempotency scope.
Connect a CI event without duplicate work
Give each intended piece of work a stable event key. For GitHub Actions, a repository, workflow run ID, and operation label identify one request. Do not include the retry attempt if rerunning a CI job should only recover the same invocation. Include an explicit new work identifier only when you intend to start another Session.
The following shell step requires curl and jq. Configure AACHAT_API_URL and AACHAT_API_KEY using the credential screen's values, and inject them through the CI environment. It constructs JSON safely rather than interpolating event text into a JSON string. Keep the same body and credential for transport retries.
# Supply AACHAT_API_URL and AACHAT_API_KEY through your CI secret settings.
# GITHUB_REPOSITORY and GITHUB_RUN_ID are provided by GitHub Actions.
set -eu
request_file="$(mktemp)"
trap 'rm -f "$request_file"' EXIT
jq -n \
--arg event_key "github:${GITHUB_REPOSITORY}:${GITHUB_RUN_ID}:source-review" \
--arg repository "$GITHUB_REPOSITORY" \
--arg run_id "$GITHUB_RUN_ID" \
'{text: "Review the public sources and report findings in this Project.",
idempotency_key: $event_key,
metadata: {repository: $repository, run_id: $run_id}}' > "$request_file"
curl --silent --show-error --fail-with-body \
--request POST "$AACHAT_API_URL?wait=started" \
--header "Authorization: Bearer $AACHAT_API_KEY" \
--header 'Content-Type: application/json' \
--data-binary "@$request_file"Do not enable shell tracing (set -x) or print the credential environment. Keep metadata to non-sensitive correlation information: it is part of the request stored by the server. The idempotency key must be nonblank and at most 200 UTF-8 bytes; metadata must be an object that serializes to at most 8192 bytes. Shorten a key deliberately if the repository and label make it too long, preserving uniqueness.
A key is scoped to its credential ID. The same key with the same normalized text, metadata, fixed launch specification, and workspace context returns the existing invocation. Metadata object key order alone does not make a different request. Changing any of those contents conflicts. A different credential is not a way to retry the same invocation safely.
Read acceptance, start, and failure separately
| Response state | Meaning | Next step |
|---|---|---|
accepted | The server recorded the invocation; start is still pending | Preserve invocation_id and the exact request. An identical retry can retrieve the current result |
started | The runtime acknowledged Session start | Follow session_id / web_url and inspect the actual result in the Session |
failed | The invocation could not complete startup | Read failure.code and failure.message, correct the cause, and use a new key only for a deliberate new attempt |
wait=started has a bounded wait and may still return accepted. It does not wait for task completion. HTTP success alone does not mean the work succeeded, and web_url can be absent until a Session is bound. Store the returned invocation ID and Session link as correlation evidence in your CI output, then check the Session's work and deliverables in aachat.
On a network timeout or lost response, retry the exact request with the same credential and key, rather than generating a new timestamp key. This can recover the existing invocation. A failed invocation remains that invocation; repeating its key does not create a fresh attempt. A wrapper that needs to retry actual failed work must make that choice explicit and inspect the previous result first.
Diagnose the failing layer
- For an unauthorized, expired, or revoked credential, check the caller's secret and the credential screen. After rotation, replace the caller's old token. Never paste a key into a support message.
- For an authority failure, check that the Project is active and that the human owner and Agent still satisfy the Project membership rules. A key cannot add membership.
- For a validation error, check nonempty text, key length, metadata shape/size, and the
waitvalue (acceptedorstarted). - For an idempotency conflict, compare the original request and execution context. Do not silently turn it into a new key and duplicate work.
- For startup failure, check the owner's runtime, fixed launch configuration, and access to the fixed workspace repo. Correct those prerequisites before deliberately starting again. If a different target or launch context is required, create an appropriate new credential.
Rotation, revocation, and expiry control use of a credential; they are not Session cancellation or rollback of work already started. Use the Session controls to manage that work. This endpoint starts a Session and does not publish or run a Workflow from a webhook.
Related pages
- Interactive sessions and workspaces: sessions
- Team / project roles: teams, projects
- Server storage boundary for credentials: trust-boundary
- Start failure diagnosis: troubleshooting