Aevatar scheduler
Create and manage recurring Aevatar runs and route to the correct scheduling resource. Use for cron, recurring Team member workflows, scheduled skill agents, typed service invocations, pause/resume, run-now, reauthorization, deletion, or credential triage. Team member automation uses its dedicated route and Agent Key; generic schedules accept typed service invocation only. External raw actor/envelope schedules are retired and must fail closed.From its SKILL.md
npx -y skills add ChronoAIProject/NyxID --skill aevatar-schedulerAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
29.6 KB, as published. Nobody here has run it
Schedule an Aevatar service on a cron
Route to the right resource FIRST — three paths, not one
"Run it on a schedule" is three different resources with three different owners, credentials, and APIs. Pick before you call anything; using the wrong one is not a shortcut, it is the wrong resource.
| The user wants to schedule… | Resource owner | Canonical entry | Credential |
|---|---|---|---|
| An already-bound Studio Team member workflow | scope → team → member | aevatar_schedule_member_workflow, or REST /api/scopes/{scopeId}/teams/{teamId}/members/{memberId}/automations | Dedicated Agent Key |
| An independent scheduled Ornn skill agent or a one-shot reminder | Scheduled agent / catalog actors | scheduled_agent_creator, then agent_builder — see aevatar-automation | Dedicated Agent Key |
| A typed service invocation | Generic platform schedule actor | Generic /api/schedules — the rest of this skill | Typed source; may be a NyxID binding exchange |
If the owner is a Team member, the canonical path is the member automation route — not generic
/api/schedules. Generic /api/schedules is a platform-level resource for typed service invocations. It
is supported, but it is not a fallback for Team member automation, and reaching for it because
you already know its shape is the most common mistake here.
Never convert between identities by route position, prefix, equality, or a familiar-looking string:
memberId, draft workflowId, publishedServiceId, UserService ID, catalog service ID, schedule
ID, and Agent Key ID are seven distinct things.
Studio Team member automation (the canonical member path)
Use aevatar_schedule_member_workflow in-session, or the owner-scoped REST route. The owner is the
exact scopeId + teamId + memberId tuple; teamId is a containment guard. The server reads the
member summary and derives publishedServiceId — you never substitute a draft workflowId, a
service ID, a binding ID, a route, a model, or a credential ID for it.
Preflight is read-only and provisions nothing. It builds a typed authorization plan from current facts: the exact member and containing Team, the bound workflow revision and prepared artifact, typed connector and NyxID capability refs, the owner LLM route/model and exact UserService ID where required, the owner-scoped NyxID authorization catalog, and policy/version/expiry/disclosure facts. Never invent grants, wildcards, node IDs, route or model choices, or caller binding evidence.
The two entry surfaces are intentionally not equivalent:
- Existing member, in-session: prefer
aevatar_schedule_member_workflow. If the member still points at an interactive revision, the tool can admit and publish an immutable durable revision, wait for that exact revision to become visible, pin it, rerun preflight, and create the automation.durable_revision_propagatingmeans wait for projection visibility and retry the same operation; it is not permission to create another member. Durable admission publishes the new revision before the final schedule preflight. If that later preflight fails, the immutable revision may remain even though no automation was created; do not claim transaction rollback or delete/rebind it without an explicit lifecycle decision. - Raw REST:
POST .../automations/preflightstays pure read. It can return non-retryable 409TEAM_AUTOMATION_DURABLE_ADMISSION_REQUIREDorTEAM_AUTOMATION_DURABLE_REQUEST_GRANT_MISMATCH, but it never upgrades or rebinds a revision. Raw create consumes a confirmed plan; it has no hidden durable-upgrade flag. - No member yet: use
aevatar_provision_workflow_scheduleonly when the user explicitly wants a new Team-owned workflow member and schedule. Do not use provisioning to clone an existing member or guess its Team.
Console flow
In Aevatar Console, open the target Scope and Team. Create a workflow member, edit the full
YAML in Workflow Studio, apply it to the draft, save, publish, and wait for published/bind-ready
state. Then open Automate / Recurring work, choose the published member, enter name, recurring
prompt, five-field cron, IANA timezone, and enabled state, preview the next runs, and authorize the
Dedicated Agent Key. Save is not publish, publish is not automation, and the accepted automation
receipt is not active state. Never substitute workflowId or publishedServiceId for the member
selected by the Console.
Fire-time prompt data
For a Chat workflow, the recurring prompt may be a fire-time JSON template. Supported placeholders are:
| Placeholder | Value at the authoritative logical fire |
|---|---|
{{@schedule.run_date}} | Local yyyy-MM-dd in the configured schedule timezone |
{{@schedule.run_year}} | Local year |
{{@schedule.run_month}} | Local month without zero padding |
{{@schedule.days_until_month_end}} | Days after the local fire date through month end; today excluded |
{{@schedule.fire_at_utc}} | Logical fire instant in UTC |
{{@schedule.timezone}} | Normalized configured timezone |
Example monthly input:
{"period_label":"{{@schedule.run_year}}年{{@schedule.run_month}}月","run_date":"{{@schedule.run_date}}","submit":false}
If any {{@schedule.*}} placeholder is present, the whole prompt must be valid JSON and every
placeholder must occur inside a JSON string value, never a property name. Unknown variables,
unmatched delimiters, invalid JSON, or an invalid timezone fail closed before schedule effects.
Prompts without schedule placeholders remain byte-for-byte unchanged. Late/catch-up execution uses
the original scheduled occurrence; run-now uses its manual fire instant. Therefore run-now can
prove template rendering, but it cannot prove cron re-arming.
Do not freeze a monthly date or days_left into a recurring prompt. Do not use model knowledge or
host wall-clock time as a business input when this contract can inject the logical fire date.
Authored write-call limitation
Admission scans the committed workflow definition, not only the branch selected by a sample
prompt. submit:false therefore does not hide authored POST/PUT/PATCH/DELETE
capability.nyxid_request call sites. On a deployment whose scheduled-operation authority port is
unavailable, preflight fails closed with
TEAM_AUTOMATION_NYXID_OPERATION_AUTHORITY_CONTRACT_UNAVAILABLE. Do not bypass it with a generic
schedule, a webhook-effect permit, or an interactive confirmation. For a harmless schedule proof,
publish a preview-only definition that physically contains no write call sites. Prefer a dedicated
acceptance member/revision and short-lived automation so the production member is not rebound by a
smoke test. If the user instead wants to replace the production member's revision, make that
lifecycle change explicit first. A real unattended write requires supported durable operation
authority, an exact admitted capability, downstream policy acceptance, and business idempotency.
Create uses credentialProvisioningKind = dedicated_scheduled_invocation_agent_key. The server
revalidates the confirmed permission digest and policy version against current sources before any
key-creation effect, then requests a fresh, targeted NyxID scope plan for the exact sorted
UserService IDs. Key creation fixes allow_all_services = false and allow_all_nodes = false —
only the targeted plan supplies allowed service IDs, node IDs, and the provider scope-plan digest.
Secret custody. The one-time raw key NyxID returns is written only to ISecretVault under
purpose scheduled.invocation-agent-key. Durable state, events, read models, logs, tools, and
public APIs expose only non-secret facts: a typed reference, Agent Key ID, expiry, authorization
fact, and credential generation. Never ask the user to paste an Agent Key, accept one in schedule
JSON, print one, store one in an Ornn package, or reconstruct one from an ID.
After a 202 — what it does and does not prove
202 Accepted (and tool status accepted / pending) means command admission only. It does
not prove credential issuance completed, the Vault write completed, activation committed, the read
model observed it, cron fired, or the workflow succeeded.
Reread the canonical owner-scoped automation and require a newer authoritative stateVersion.
A ready automation normally shows:
authorizationStatus == activecredentialSourceKind == scheduled_invocation_agent_keyenabled == true(firing)- a
credentialExpiresAtUtclater than the next intended fire (and long enough for the recurring operating window) - a positive
credentialGeneration revocationPending == false
Also require the exact owner tuple, target revision, cron, timezone, and unrendered prompt template
to match what was authorized. Then verify at least one automatic fire with manual == false,
empty current lastError / lastErrorCode, and a committed successful workflow run. Prefer two
consecutive automatic fires for a short-lived smoke schedule so re-arming is proven. Delete or
pause high-frequency smoke schedules after acceptance.
For a Team member, correlate the schedule to committed runs with
GET /api/scopes/{scopeId}/members/{memberId}/runs?take=50&scheduleId={scheduleId} and then inspect
the exact run. Require terminal success and, when testing a fire-time template, verify the committed
run input contains rendered values and no residual {{@schedule. token. A Dispatched fire is not
a completed workflow run.
failureCount is a lifetime counter, not current health. A successful fire clears current
lastError while historical failures remain counted. Do not label a recovered automation as
currently failing from failureCount > 0 alone.
Exact REST lifecycle
The current member-owned surface is nested under the full owner tuple:
POST /api/scopes/{scopeId}/teams/{teamId}/members/{memberId}/automations/preflight
GET /api/scopes/{scopeId}/teams/{teamId}/members/{memberId}/automations
POST /api/scopes/{scopeId}/teams/{teamId}/members/{memberId}/automations
GET /api/scopes/{scopeId}/teams/{teamId}/members/{memberId}/automations/{scheduleId}
PUT /api/scopes/{scopeId}/teams/{teamId}/members/{memberId}/automations/{scheduleId}
POST .../{scheduleId}/reauthorize|pause|resume|run-now|retry-revocation
DELETE .../{scheduleId}
Preflight with {scheduleCron,scheduleTimezone,prompt,displayName,enabled}. Create/reauthorize must
copy the current plan's permissionDigest and credentialPolicy.policyVersion, set
credentialProvisioningKind=dedicated_scheduled_invocation_agent_key, and use stable, unique
operationId plus idempotencyKey. Lifecycle actions also require those two operation identities.
Never retry a lost mutation with new identities; reread the same owner resource first.
For owner-aware diagnostic reads through generic /api/schedules, use
ownerKind=studio_member_automation plus ownerScopeId; add ownerTeamId for Team scope, and the
full ownerScopeId + ownerTeamId + ownerMemberId tuple for a member or exact get. Legacy
scopeId/teamId/memberId query names are invalid. Keep legacy generic schedules in a separate
migration view; never mutate a Team automation through an ownerless legacy action.
active is credential health; enabled is firing. They are independent dimensions — do not
report one as the other.
Fire and how the workflow gets the credential
Cron and run-now use the same active credential generation. Run-now proves a manual fire only; it never proves cron-origin execution.
Aevatar projects a borrowed DurableCallerCredentialRef into the workflow caller context. The raw
Agent Key is not copied into workflow state. Every LLM, tool, and connector path that needs the
caller credential resolves that reference through ISecretVault at the moment it is used, and
resolution is fail-closed for an absent, expired, revoked, mismatched, or malformed reference.
So do not describe this path as one 300-second broker token minted at fire and shared for the whole run. That describes a generic schedule using a NyxID binding source — a different credential source with different lifetime semantics.
Lifecycle operations
- Pause / resume. Pause disables future firing but preserves the active credential. Resume re-enables firing if the credential is still usable. Neither is a revoke or a reauthorization — never recreate or revoke a key to resume.
- Update. Cron, timezone, prompt, display name, and enabled changes revalidate current authorization facts. An update never silently expands key grants or swaps the active credential. Authorization drift surfaces as an explicit reauthorization requirement.
- Reauthorize (this is what a new external service dependency requires). Start from a new preflight and confirmation, provision a new dedicated key generation from the newly validated exact plan, and only after the replacement generation is committed revoke the old one. Never mutate an existing key in place to widen its authority.
- Delete. The tombstone and revocation intent commit before external cleanup. NyxID key
revocation and Vault secret revocation are independent durable tracks. The row stays visible as
deleting/revocation_pendingwhile either required track is incomplete. Retry reuses the original delete operation and idempotency identity plus fresh owner authority — it never synthesizes a new delete, reauthorization, or credential. Only completion of all required tracks lets the automation disappear. - Expiry and drift. An expired, missing, revoked, unresolvable, or authorization-mismatched key
fails closed: the automation moves toward
needs_authorizationand future fire leases are canceled. It must never fall back to an interactive user bearer, a Host default, an inferred binding, an inferred service, or a wildcard grant.
Stable recovery
- Lost create response → reread by exact owner and original operation identity; never create a second schedule with new identities.
authorization_plan_changed→ rerun preflight before retrying.needs_authorization→ new preflight and explicit reauthorize.revocation_pending→ retry revocation with the original delete operation identity.- Missing owner binding or authorization catalog evidence → fail closed and report the prerequisite; never invent evidence or trigger projection repair.
TEAM_AUTOMATION_AUTHORIZATION_ROUTE_UNRESOLVED/ toolauthorization_catalog_route_unresolved→ non-retryable 409. Preserve the sanitizedrefreshFailureCodeand exactrequiredUserServiceIds, stop retrying refresh/preflight, and fix or deactivate the unresolved configured route before trying again. A required service ID can be affected by a same-catalog active peer; it is not by itself proof of the offending row. Never delete or reconnect unrelated services.- Projection pending → report eventual visibility and the required version; never replay or prime projection from the query path.
Generic typed-service schedule (/api/schedules)
Everything below is the generic typed service-invocation resource. External callers cannot
schedule a raw actor EventEnvelope; that target is retired and rejected. Never use an envelope
as a fallback for a Team member.
You create a schedule that fires a published service on a cron expression,
authenticated as you (the scope owner) through NyxID. Publish the service first
(aevatar-service-publisher) — you need its identity, an endpoint, and the payload type.
Bootstrap
# Drive aevatar THROUGH the NyxID broker: it injects your scope_id claim AND auto-refreshes your
# token. A raw curl to the aevatar backend with ~/.nyxid/access_token resolves NO scope
# (scopeResolved:false) and the stored token expires — it is not a usable path.
# Prerequisite once: the `aevatar` service must be connected — `nyxid service add aevatar`.
# NOTE: the aevatar backend requires `Content-Type: application/json` on writes (POST/PUT) —
# omit it and every write returns HTTP 415 Unsupported Media Type. The helper sets it on
# every call (harmless on bodyless GETs), so the POST/PUT examples below work as written.
aev() { nyxid proxy request aevatar "$@" -H 'Content-Type: application/json'; } # aev "<path>" [-m POST|PUT|DELETE] [-d '<json>'] [--stream]
scopeId=$(aev "api/studio/context" | jq -r .scopeId)
jqis only for convenience — any JSON reader works (replace| jq -r .scopeIdwith| python3 -c 'import sys,json;print(json.load(sys.stdin)["scopeId"])'). All calls go through the NyxID broker (nyxid proxy request aevatar), which injects your scope_id claim and auto-refreshes the token. Reminder: thescopeOwnerNyxIdprecondition below cannot be satisfied by a bare NyxID CLI token — it needs the owner's interactive console NyxID login (broker binding), or creation 400s.
Gather the target (one call: the scope services list)
GET /api/scopes/{scopeId}/services returns everything you need per service — copy it off
the entry for your service:
aev "api/scopes/$scopeId/services" \
| jq '.[] | {tenantId, appId, namespace, serviceId, defaultServingRevisionId, invokeReady,
endpoints: [.endpoints[] | {endpointId, requestTypeUrl}]}'
- identity — copy the explicit 4-tuple
{tenantId, appId, namespace, serviceId}from the service read model or memberpublished-serviceassociation. Never deriveserviceIdfrommemberId, a prefix, route position, or string equality. - endpointId + payloadTypeUrl — from
endpoints[](payloadTypeUrl= the endpoint'srequestTypeUrl). A workflow member's default endpoint ischatwithtype.googleapis.com/aevatar.ai.ChatRequestEvent. - revisionId — use the service's
defaultServingRevisionId. Required whenever you sendpayloadJson(see below). - payloadJson — the request body as a JSON string (or
payloadBase64for a packed proto). For a chat endpoint,{"prompt":"…"}is accepted. - Confirm
invokeReadyistruebefore scheduling — a schedule against a not-yet-serving service will fire into nothing.
Preview the cron first (no clock guessing)
aev "api/schedules/preview" -m POST \
-d '{"cronExpression":"0 9 * * 1-5","timezone":"Asia/Shanghai","count":"5"}' | jq .
Returns the next N fire times so you can confirm the expression means what the user wants.
Use a real IANA timezone; the engine has no implicit local time.
Precondition: the scope owner needs a NyxID owner (broker) binding
A scheduled service fire happens later, after your current token has expired, so the
platform must be able to re-mint the scope owner's NyxID credential at fire time. That
requires an authenticated NyxID owner binding (urn:nyxid:scope:broker_binding),
established by signing in through the Aevatar console / studio NyxID login (a browser PKCE
authorization_code flow → POST /api/auth/nyxid/finalize). A plain NyxID-CLI token is
not sufficient. Create-time validation does a real token mint, so a missing/revoked
binding fails fast at create with one of:
HTTP 400 — "Authenticated NyxID owner binding is required for scope owner schedule auth…" HTTP 400 — "NyxID binding was revoked for the scheduled subject. (Parameter 'configuration')"
Diagnose from the typed create/preflight error and the Aevatar read model. Never read a stored
access token, construct an Authorization header, or call NyxID/Aevatar directly with curl.
A clean console re-login (fully
logged out first) refreshes a revoked binding — finalize replaces it on the revoked/stale
probe path — so that usually clears it; an SSO-cached login may not re-run finalize.
There is no CLI / headless path to establish this binding (NyxID mints broker bindings
only via the authorization_code grant; the only Aevatar writer is the browser finalize).
Tracked at aevatarAI/aevatar#2491 — do not promise a CLI-only way to create a
scopeOwnerNyxId schedule until it lands.
CLI-only alternative: skip the Aevatar scheduler entirely
For a recurring run without the browser console, don't use scopeOwnerNyxId scheduling.
An explicitly authorized external timer may invoke the already-published member/team endpoint
through NyxID using a dedicated minimum-authority API key kept in that timer's secret manager.
Never print the key, put it in workflow YAML, export it into a shared shell history, or expose it
in diagnostics.
The member invoke endpoint carries scopeId in its path, so it runs even though a bare API
key reports scopeResolved:false on the generic api/studio/context call. The same pattern
works for event-driven external triggers such as Lark Base automation's "send HTTP request"
action: store the NyxID API key as the external system's secret and POST to the explicit
member/team invoke path. This is not Aevatar externalExposure; externalExposure is only
needed when the workflow must be registered as a reusable NyxID connector/slug. Trade-off:
the timer or event sender runs outside Aevatar (a cloud cron would live in Aevatar; this does not).
The binding-exchange fire-time credential lives 5 minutes — design the run around it
Scope of this section: generic /api/schedules using a NyxID binding source only. Read
credentialSourceKind before applying any of it. If the run reports
credentialSourceKind = scheduled_invocation_agent_key, none of the numbers below apply — jump to
Diagnosing a scheduled-run credential failure.
For a binding-exchange source, at every fire Aevatar exchanges the stored broker binding for a
fresh access token (OAuth token-exchange,
subject_token_type=urn:nyxid:params:oauth:token-type:binding-id) and projects that one token
into the run as its caller credential — minted once, shared by the whole run
(aevatar: agents/Aevatar.GAgents.Channel.Identity/Broker/NyxIdRemoteCapabilityBroker.cs,
src/platform/Aevatar.GAgentService.Infrastructure/Schedules/ScheduledServiceInvocationDispatchPort.cs).
Broker-issued tokens are pinned to BROKER_ACCESS_TTL_SECS = 300 (NyxID
backend/src/services/oauth_broker_service.rs) so a revoked binding stops working within
5 minutes without introspection. Deliberate design, not a bug.
Consequence: every NyxID-authenticated step in such a fired run must complete within ~5 minutes
of fire. In a longer run, late steps failing with token_expired is the expected platform
behavior — keep those scheduled runs short, front-load the NyxID-authenticated steps, or split long
pipelines into separate schedules. Do not "fix" it by retrying the same run shape.
Diagnosing a scheduled-run credential failure
Identify the typed credential source before interpreting any 401 or token_expired. Gather the
canonical path and owner tuple, credentialSourceKind, authorizationStatus,
credentialExpiresAtUtc, credentialGeneration, stateVersion, lastAuthorizationErrorCode,
revocationPending, nyxIdRevocationStatus, vaultRevocationStatus, and the exact run/tool
failure and timestamp.
credentialSourceKind | Diagnosis |
|---|---|
nyxid_binding_exchange | The 300 s broker TTL above is the right hypothesis. Confirm with the exchanged token's exp − iat or the repo constant, then fix run shape. |
scheduled_invocation_agent_key | Do not diagnose a fixed five-minute broker expiry merely because the call was scheduled. Inspect key expiry, Vault resolution, reference integrity, committed caller authority, authorization fact, the exact service/node grants versus what the failing step actually called, credentialGeneration, and revocation state. Then look at the downstream service's own token. A token_expired six minutes after fire on this source is not explained by the 300 s constant — that constant belongs to a different credential class. |
Token classes are not interchangeable — never quote one class's TTL for another. Your
interactive login token lives for hours (JWT_ACCESS_TTL_SECS, a deployment config — code
default is 900 s); a binding-exchange run credential lives 300 s (fixed constant); a dedicated
scheduled-invocation Agent Key is Vault-held with a policy-governed expiry (a scheduled skill
agent's default projected lifetime is ~90 days); NyxID API keys (nyxid api-key create) don't
expire at all. When diagnosing any expiry, read the lifetime instead of recalling it: decode the
JWT actually in hand and report exp − iat (numbers only — never print the token), cite the owning
repo's constant, or read the automation's credentialExpiresAtUtc and credentialGeneration.
Never emit secret material in any report, log excerpt, artifact, or message: no raw Agent Key, bearer/access/refresh/delegation/service-account token, Vault reference or ciphertext, permission digest, unfiltered API-key inventory, or authorization header. Stable resource IDs may be reported for management or cleanup, but never as secret material and never to derive another identity.
Create the schedule
aev "api/schedules" -m POST -d "{
\"displayName\": \"Weekday 9am run\",
\"cronExpression\": \"0 9 * * 1-5\",
\"timezone\": \"Asia/Shanghai\",
\"enabled\": true,
\"serviceInvocation\": {
\"identity\": { \"tenantId\": \"<from-service-contract>\", \"appId\": \"<from-service-contract>\", \"namespace\": \"<from-service-contract>\", \"serviceId\": \"<published-service-id-from-contract>\" },
\"endpointId\": \"chat\",
\"payloadTypeUrl\": \"type.googleapis.com/aevatar.ai.ChatRequestEvent\",
\"payloadJson\": $(jq -nc '{prompt:"do the thing"} | tojson'),
\"revisionId\": \"<defaultServingRevisionId>\",
\"auth\": { \"scopeOwnerNyxId\": { \"scope\": \"proxy\" } }
}
}"
ScheduledDispatchConfigurationHttpRequest: cronExpression (required); displayName?,
timezone?, enabled (default true), headers? (string map), and exactly one
serviceInvocation target. Any external envelope target must be rejected.
payloadJsonrequiresrevisionId. If you supplypayloadJsonwithout arevisionId(and the service has no active serving revision), creation fails with 400 "payloadJson requires a revisionId; provide one explicitly or activate a serving revision." Pass the service'sdefaultServingRevisionId.
Workflow-member services: use
payloadBase64, notpayloadJson. Amember-<id>service produced by a Studio bind (the common workflow path) carries a serving revision with no protocol descriptor, sopayloadJsonfails creation with 400 "payloadTypeUrl '…ChatRequestEvent' could not be resolved: revision '…' has no protocol descriptor set." The fix is to send the request as a packed proto inpayloadBase64instead — it bypasses the descriptor-based JSON encoding. The streaming invoke (…/invoke/chat:stream) accepts the{"prompt":"…"}shorthand via a shim, but the scheduler's typed path does not. For aChatRequestEventwithpromptat field 1:# python3 -c "import base64;print(base64.b64encode(bytes([0x0a,len(p:=b'do the thing')])+p).decode())" # → swap the `payloadJson` line for: "payloadBase64": "CgxkbyB0aGUgdGhpbmc=",If your workflow ignores the prompt (e.g. a self-contained poll), any valid
ChatRequestEventpayload triggers the run.
Auth (serviceInvocation.auth)
scopeOwnerNyxId: { scope }— fire as the scope owner, re-minting their NyxID at fire time. The right choice for owner-run schedules, but it requires the owner's broker binding (see Precondition above), otherwise creation 400s.senderNyxId: { subject: { platform, externalUserId, tenant? }, scope }— fire as a specific external subject. Only when the schedule must run as someone other than the owner, and that subject already has a durable NyxID binding — otherwise the fire fails at credential-mint time.
Verify, then manage
sid=$(...) # scheduleId from the create response
aev "api/schedules" | jq '.[] | {scheduleId, displayName, cronExpression, enabled, nextFireUtc}'
aev "api/schedules/$sid" | jq .
aev "api/schedules/$sid:run-now" -m POST # fire once immediately to test
aev "api/schedules/$sid:disable" -m POST # pause
aev "api/schedules/$sid:enable" -m POST # resume
aev "api/schedules/$sid" -m PUT -d '{ ...updated configuration... }'
aev "api/schedules/$sid" -m DELETE # remove
Note the action verbs use a colon (/{scheduleId}:run-now), not a slash.
After :run-now, confirm the fire actually executed — check the service's runs
(GET /api/scopes/{scopeId}/services/{serviceId}/runs) or the observatory
(GET /api/workflow/observatory/runs). A 2xx on the schedule call means accepted, not
succeeded; a fire can still fail later at credential-mint or execution time, so read the
run back before reporting success.
Next
- Need to (re)publish the target service?
aevatar-service-publisher. - Want the whole picture?
aevatar-platform-map.
If you cannot complete a step server-side after a real attempt, hand the original request back to your caller rather than fabricating — see the fallback skill in this family.