Skip to content

GET /automation/rules

The workspace's automation rules

Newest first, and only the rules the caller may see: a member manages their own rules, and an administrator the scope's. Deleted rules are not among them - the deletion is soft so that the runs a rule produced stay readable, not so that the rule stays listed.

Query parameters

NameTypeRequiredDescription
cursorstring optional
sizeinteger
  • default: 50
  • minimum: 1
  • maximum: 200
optional
enabledboolean optionalNarrow to the rules that are switched on, or to the ones that are not.

Responses

Example bash
curl -X GET "$HUBTASK_URL/automation/rules" \  -H "Authorization: Bearer $HUBTASK_TOKEN"

POST /automation/rules

Write a rule

A rule is created switched off. Enabling it is its own call and its own audit entry: writing what a rule would do and letting it loose on the workspace are two decisions, and a rule that ran the moment it was saved would give nobody the chance to read it back first. Writing a rule needs the automation permission at its scope and the rights its own actions need. Without the second half a member with automation rights and nothing else would write a rule that runs as a generously-scoped service account and does what they may not. What this build refuses, and says so by code rather than by silence: a condition that does not compile, or that names something this build does not publish; an action naming a kind this build does not serve; a parameter the action's use case does not declare.

Headers

NameTypeRequiredDescription
Idempotency-Keystring (uuid) optionalA UUID; identical requests return the same result for 24 h.

Request body application/json — AutomationRuleCreate

NameTypeRequiredDescription
namestring
  • maxLength: 200
required
scopeRuleScope requiredWhere the rule applies: the whole workspace, one hub, or one collection.
typestring
  • TENANT
  • HUB
  • COLLECTION
required
idstring (uuid) optionalThe hub or the collection.
run_asstring (uuid) required
triggerRuleTrigger requiredWhat starts a run.
kindstring
  • EVENT
  • SCHEDULE
  • RELATIVE_DATE
  • INBOUND_WEBHOOK
  • MANUAL
  • JUMBLE_ENTRY
required
event_typestring optionalEVENT only, and required there: the full type, de.hubtask.work.item.overdue.v1.
changed_fieldsarray of string optionalEVENT only, optional.
rrulestring optionalSCHEDULE only, and required there: an RFC 5545 RRULE.
timezonestring optionalSCHEDULE only, and required there: an IANA zone.
anchorstring
  • DUE_DATE
  • CREATED_AT
optionalRELATIVE_DATE only, and required there: which instant the offset is from.
offsetstring optionalRELATIVE_DATE only, and required there: a signed ISO 8601 duration, -PT24H for "24 hours before".
conditionsarray of RuleCondition optional
exprstring requiredA CEL expression, compiled when the rule is written rather than when it runs: an expression that does not compile, or that names something this build does not publish, is a field error under its own index rather than a rule that fails silently at three in the morning.
actionsarray of RuleAction required
kindstring requiredADD_LABEL, ASSIGN, CREATE_ITEM, … A kind this build does not serve is refused by name rather than stored to be ignored.
paramsobject optionalThe action's parameters.
throttleRuleThrottle optionalWhat bounds a storm.
max_runs_per_hourinteger
  • minimum: 1
  • maximum: 10000
optional
dedupe_key_exprstring optionalAn expression whose value collapses runs that mean the same thing - two events about one entry within the window are one run rather than two.
on_errorstring
  • STOP
  • CONTINUE
  • RETRY
  • default: "STOP"
optional

Responses

Example bash
curl -X POST "$HUBTASK_URL/automation/rules" \  -H "Authorization: Bearer $HUBTASK_TOKEN" \  -H "Idempotency-Key: $(uuidgen)" \  -H "Content-Type: application/json" \  -d '{  "name": "string",  "scope": {    "type": "TENANT"  },  "run_as": "018f6e2a-2b7c-7c1e-9b1a-3d5e6f7a8b9c",  "trigger": {    "kind": "EVENT"  },  "conditions": [    {      "expr": "string"    }  ],  "actions": [    {      "kind": "string"    }  ],  "throttle": {},  "on_error": "STOP"}'

GET /automation/rules/{ruleId}

One rule, as it stands

Path parameters

NameTypeRequiredDescription
ruleIdstring (uuid) required

Responses

Example bash
curl -X GET "$HUBTASK_URL/automation/rules/{ruleId}" \  -H "Authorization: Bearer $HUBTASK_TOKEN"

PATCH /automation/rules/{ruleId}

Change what a rule does

An omitted field is left alone. enabled is not among the fields: switching a rule on or off is its own call, so that the audit trail says which of the two somebody did rather than leaving it to be inferred from a change set. The same two-sided permission check as creating one, against the rule as it would be afterwards - a rule may not be edited into doing something its writer may not do.

Path parameters

NameTypeRequiredDescription
ruleIdstring (uuid) required

Request body application/merge-patch+json — AutomationRuleUpdate

NameTypeRequiredDescription
namestring
  • maxLength: 200
optional
scopeRuleScope optionalWhere the rule applies: the whole workspace, one hub, or one collection.
typestring
  • TENANT
  • HUB
  • COLLECTION
required
idstring (uuid) optionalThe hub or the collection.
run_asstring (uuid) optional
triggerRuleTrigger optionalWhat starts a run.
kindstring
  • EVENT
  • SCHEDULE
  • RELATIVE_DATE
  • INBOUND_WEBHOOK
  • MANUAL
  • JUMBLE_ENTRY
required
event_typestring optionalEVENT only, and required there: the full type, de.hubtask.work.item.overdue.v1.
changed_fieldsarray of string optionalEVENT only, optional.
rrulestring optionalSCHEDULE only, and required there: an RFC 5545 RRULE.
timezonestring optionalSCHEDULE only, and required there: an IANA zone.
anchorstring
  • DUE_DATE
  • CREATED_AT
optionalRELATIVE_DATE only, and required there: which instant the offset is from.
offsetstring optionalRELATIVE_DATE only, and required there: a signed ISO 8601 duration, -PT24H for "24 hours before".
conditionsarray of RuleCondition optional
exprstring requiredA CEL expression, compiled when the rule is written rather than when it runs: an expression that does not compile, or that names something this build does not publish, is a field error under its own index rather than a rule that fails silently at three in the morning.
actionsarray of RuleAction optional
kindstring requiredADD_LABEL, ASSIGN, CREATE_ITEM, … A kind this build does not serve is refused by name rather than stored to be ignored.
paramsobject optionalThe action's parameters.
throttleRuleThrottle optionalWhat bounds a storm.
max_runs_per_hourinteger
  • minimum: 1
  • maximum: 10000
optional
dedupe_key_exprstring optionalAn expression whose value collapses runs that mean the same thing - two events about one entry within the window are one run rather than two.
on_errorstring
  • STOP
  • CONTINUE
  • RETRY
optional
expected_versioninteger optionalThe version last read.

Responses

Example bash
curl -X PATCH "$HUBTASK_URL/automation/rules/{ruleId}" \  -H "Authorization: Bearer $HUBTASK_TOKEN" \  -H "Content-Type: application/merge-patch+json" \  -d '{  "name": "string",  "scope": {    "type": "TENANT"  },  "run_as": "018f6e2a-2b7c-7c1e-9b1a-3d5e6f7a8b9c",  "trigger": {    "kind": "EVENT"  },  "conditions": [    {      "expr": "string"    }  ],  "actions": [    {      "kind": "string"    }  ],  "throttle": {},  "on_error": "STOP",  "expected_version": 1}'

DELETE /automation/rules/{ruleId}

Remove a rule

A soft delete. The rule stops matching anything at once and disappears from the listing, and the runs it produced stay readable: a run log whose rule vanished would be a record of actions nobody can account for.

Path parameters

NameTypeRequiredDescription
ruleIdstring (uuid) required

Responses

  • 204 Removed
  • 4XX An error per RFC 9457 Problem
Example bash
curl -X DELETE "$HUBTASK_URL/automation/rules/{ruleId}" \  -H "Authorization: Bearer $HUBTASK_TOKEN"

POST /automation/rules/{ruleId}:enable

Switch a rule on

Its own call and its own audit entry, on both sides. Letting a rule act on the workspace is the decision worth recording, and the permission is re-checked here rather than trusted from whenever the rule was written: the writer's rights may have narrowed since. Enabling also clears the failure counter, because a rule somebody has looked at and turned back on is a rule whose run of failures has been dealt with.

Path parameters

NameTypeRequiredDescription
ruleIdstring (uuid) required

Headers

NameTypeRequiredDescription
Idempotency-Keystring (uuid) optionalA UUID; identical requests return the same result for 24 h.

Responses

Example bash
curl -X POST "$HUBTASK_URL/automation/rules/{ruleId}:enable" \  -H "Authorization: Bearer $HUBTASK_TOKEN" \  -H "Idempotency-Key: $(uuidgen)"

POST /automation/rules/{ruleId}:disable

Switch a rule off

The rule stays, and stops acting. What a run of failures does automatically, a person can do deliberately - and the trail says which of the two it was.

Path parameters

NameTypeRequiredDescription
ruleIdstring (uuid) required

Headers

NameTypeRequiredDescription
Idempotency-Keystring (uuid) optionalA UUID; identical requests return the same result for 24 h.

Responses

Example bash
curl -X POST "$HUBTASK_URL/automation/rules/{ruleId}:disable" \  -H "Authorization: Bearer $HUBTASK_TOKEN" \  -H "Idempotency-Key: $(uuidgen)"

POST /automation/rules:check

Check every rule of the workspace against what exists now

Every rule the caller may read, its references resolved - the trigger's event type, each action's kind and parameter keys, each condition, the account it runs as, and every identifier a parameter carries - against what this installation and this workspace have now. What is found is written on the rules (findings, checked_at) and answered here; a rule that cannot run is switched off, audited and its author told, exactly as five failed runs would. Nothing else is written. The rules screen calls this when it opens, which is what makes "after an update, the rules that need attention are shown" true without anything enumerating tenants. A deletion of something a rule may name runs the same check for the workspace by itself.

Responses

  • 200 The rules, each with what the check found
  • 4XX An error per RFC 9457 Problem
Example bash
curl -X POST "$HUBTASK_URL/automation/rules:check" \  -H "Authorization: Bearer $HUBTASK_TOKEN"

POST /automation/rules:test

Dry-run a rule against a sample event

A dry run: a sample event in, which conditions matched and which actions would run out - and no side effects, with the restore's dry-run discipline behind the promise: nothing below this route opens a writing transaction. No action dispatches, nothing is queued, and the run log records nothing. Test either a stored rule by its identifier or a definition you are about to save - the same document POST /automation/rules takes, checked by the same validation, so a rule the dry run accepts is a rule the create will accept. Both arms of every branch are reported with whether they would run, which is more than a real run's log shows: the run records the path it took, and the dry run answers the question "and what if it had not".

Request body application/json — RuleTest

NameTypeRequiredDescription
rule_idstring (uuid) optionalA stored rule to test.
ruleAutomationRuleCreate optional
namestring
  • maxLength: 200
required
scopeRuleScope requiredWhere the rule applies: the whole workspace, one hub, or one collection.
typestring
  • TENANT
  • HUB
  • COLLECTION
required
idstring (uuid) optionalThe hub or the collection.
run_asstring (uuid) required
triggerRuleTrigger requiredWhat starts a run.
kindstring
  • EVENT
  • SCHEDULE
  • RELATIVE_DATE
  • INBOUND_WEBHOOK
  • MANUAL
  • JUMBLE_ENTRY
required
event_typestring optionalEVENT only, and required there: the full type, de.hubtask.work.item.overdue.v1.
changed_fieldsarray of string optionalEVENT only, optional.
rrulestring optionalSCHEDULE only, and required there: an RFC 5545 RRULE.
timezonestring optionalSCHEDULE only, and required there: an IANA zone.
anchorstring
  • DUE_DATE
  • CREATED_AT
optionalRELATIVE_DATE only, and required there: which instant the offset is from.
offsetstring optionalRELATIVE_DATE only, and required there: a signed ISO 8601 duration, -PT24H for "24 hours before".
conditionsarray of RuleCondition optional
exprstring requiredA CEL expression, compiled when the rule is written rather than when it runs: an expression that does not compile, or that names something this build does not publish, is a field error under its own index rather than a rule that fails silently at three in the morning.
actionsarray of RuleAction required
kindstring requiredADD_LABEL, ASSIGN, CREATE_ITEM, … A kind this build does not serve is refused by name rather than stored to be ignored.
paramsobject optionalThe action's parameters.
throttleRuleThrottle optionalWhat bounds a storm.
max_runs_per_hourinteger
  • minimum: 1
  • maximum: 10000
optional
dedupe_key_exprstring optionalAn expression whose value collapses runs that mean the same thing - two events about one entry within the window are one run rather than two.
on_errorstring
  • STOP
  • CONTINUE
  • RETRY
  • default: "STOP"
optional
sample_eventRuleTestEvent optionalThe sample event, as the CEL event variable will read it.
typestring requiredThe event type, e.g.
subjectstring optionalWhat the event is about, e.g.
payloadobject optionalThe event's payload document.
payloadobject optionalThe body an inbound delivery would have carried, as the CEL payload variable reads it.

Responses

Example bash
curl -X POST "$HUBTASK_URL/automation/rules:test" \  -H "Authorization: Bearer $HUBTASK_TOKEN" \  -H "Content-Type: application/json" \  -d '{  "rule_id": "018f6e2a-2b7c-7c1e-9b1a-3d5e6f7a8b9c",  "rule": {    "name": "string",    "scope": {      "type": "TENANT"    },    "run_as": "018f6e2a-2b7c-7c1e-9b1a-3d5e6f7a8b9c",    "trigger": {      "kind": "EVENT"    },    "actions": [      {}    ]  },  "sample_event": {    "type": "string"  },  "payload": {}}'

POST /automation/rules/{ruleId}:trigger

Run a rule now

The MANUAL trigger: the one of the six a person pulls. It queues a run into the same engine every other trigger produces into - the conditions are evaluated, the actions dispatch as the rule's run_as account, and the loop bound and the throttle apply exactly as they do to an event's run. The run records who pulled it, which is the whole reason this is its own call rather than a flag on something else: a rule that acted because somebody asked and a rule that acted because a deadline passed are two different entries in the log. Only a rule whose trigger is MANUAL can be started this way, and only an enabled one. Both refusals name themselves rather than answering an empty success - a call that appears to work and does nothing is the failure this refusal exists to avoid. The answer carries the identifier the run will have. It is a 202: the run happens on a worker, so GET /automation/runs/{runId} answers 404 until it starts and the whole log afterwards.

Path parameters

NameTypeRequiredDescription
ruleIdstring (uuid) required

Headers

NameTypeRequiredDescription
Idempotency-Keystring (uuid) optionalA UUID; identical requests return the same result for 24 h.

Responses

Example bash
curl -X POST "$HUBTASK_URL/automation/rules/{ruleId}:trigger" \  -H "Authorization: Bearer $HUBTASK_TOKEN" \  -H "Idempotency-Key: $(uuidgen)"

POST /automation/rules/{ruleId}:rotate-inbound-token

Mint the address an inbound-webhook rule answers on

The INBOUND_WEBHOOK trigger's credential: a token-protected URL per rule, minted here and answered once. It is stored hashed under its own purpose label, so no later read can produce it. Rotating is how one is revoked. There is exactly one address per rule and the replacement happens in a single statement, so the old token and the new one never both open the rule - which is what "revocable by rotating" has to mean. Somebody who wants to revoke without a replacement switches the rule off. Only a rule whose trigger is INBOUND_WEBHOOK. An address on any other rule would be a credential that opens nothing, handed out as though it worked.

Path parameters

NameTypeRequiredDescription
ruleIdstring (uuid) required

Headers

NameTypeRequiredDescription
Idempotency-Keystring (uuid) optionalA UUID; identical requests return the same result for 24 h.

Responses

Example bash
curl -X POST "$HUBTASK_URL/automation/rules/{ruleId}:rotate-inbound-token" \  -H "Authorization: Bearer $HUBTASK_TOKEN" \  -H "Idempotency-Key: $(uuidgen)"

POST /automation/inbound/{token}

Start a rule from outside

The other half of the INBOUND_WEBHOOK trigger: an external system posts here and the rule's run begins, in the same engine every other trigger produces into. This authenticates the rule, never a person. There is no account behind the token and it grants nothing beyond starting that one rule - the run carries no actor, because naming one would invent an author for something nobody did. What the run may then do is its run_as account's business, checked per action exactly as it is for every other trigger. The body is a JSON object and enters the condition language as payload, as data: it is never rendered as an instruction to anything. It is bounded well below the request limit, because what bounds a transfer and what bounds an evaluation are two different numbers - anything larger is refused rather than truncated. An unknown token, a rotated one, a rule that has been deleted, a rule that is switched off and a rule whose trigger is no longer an inbound webhook all answer the same 404 with the same body. Distinguishing them would answer questions to whoever is trying tokens.

Path parameters

NameTypeRequiredDescription
tokenstring requiredThe rule's inbound token, which is the whole credential: this route carries no bearer authentication, and the URL is what the calling system stores.

Request body application/json

Responses

Example bash
curl -X POST "$HUBTASK_URL/automation/inbound/{token}" \  -H "Content-Type: application/json" \  -d '{}'

GET /automation/runs

What the rules have done

Every run, newest first: which rule, which event started it, how its conditions answered, what each action did, and what went wrong if anything did. This is the run log - retrievable, filterable, and the only place a person can find out why a rule did or did not act. A run outlives the rule that produced it. Deleting a rule is soft for exactly this reason: a record of actions nobody can account for would be worse than the rule staying visible.

Query parameters

NameTypeRequiredDescription
cursorstring optional
sizeinteger
  • default: 50
  • minimum: 1
  • maximum: 200
optional
rule_idstring (uuid) optionalNarrow to one rule.
statusstring
  • RUNNING
  • WAITING
  • SUCCEEDED
  • SKIPPED
  • FAILED
  • ABORTED_LOOP
  • THROTTLED
optionalNarrow to one outcome.
triggerstring
  • EVENT
  • SCHEDULE
  • RELATIVE_DATE
  • INBOUND_WEBHOOK
  • MANUAL
  • JUMBLE_ENTRY
optionalNarrow to one way of starting.
fromstring (date-time) optionalThe start of the window, inclusive, on started_at.
tostring (date-time) optionalThe end of the window, exclusive, on started_at.

Responses

Example bash
curl -X GET "$HUBTASK_URL/automation/runs" \  -H "Authorization: Bearer $HUBTASK_TOKEN"

GET /automation/runs/{runId}

One run, with every condition and every action

Path parameters

NameTypeRequiredDescription
runIdstring (uuid) required

Responses

Example bash
curl -X GET "$HUBTASK_URL/automation/runs/{runId}" \  -H "Authorization: Bearer $HUBTASK_TOKEN"

POST /automation/runs/{runId}:replay

Complete a failed run

Re-executes a failed run's remaining actions under the same idempotency keys - which is what makes this a completion rather than a duplication: an action the original run finished finds its key already claimed and does nothing again, and only what never happened happens now. Only a FAILED run can be replayed, and only while its rule is enabled: a waiting run resumes by itself, a skipped or throttled one did what its rule says, and a disabled rule must not act. The conditions are evaluated again against the world as it stands - the answer that decides an action is the answer of the day it runs - and the replay is audited with the replayer, because somebody looked at a failure and decided the world is ready for the rest. The answer carries the identifier the replay's run will have, exactly as :trigger does.

Path parameters

NameTypeRequiredDescription
runIdstring (uuid) required

Headers

NameTypeRequiredDescription
Idempotency-Keystring (uuid) optionalA UUID; identical requests return the same result for 24 h.

Responses

Example bash
curl -X POST "$HUBTASK_URL/automation/runs/{runId}:replay" \  -H "Authorization: Bearer $HUBTASK_TOKEN" \  -H "Idempotency-Key: $(uuidgen)"

POST /integrations/http-requests

Call an external HTTP address

One outbound call, through the guarded client: private and link-local ranges are refused unless the installation released them, redirects are limited, the call has a deadline and the response a size cap - and the response is then discarded, because a rule cannot read an answer. The header secret is sealed at rest and masked everywhere after creation. This is also what a rule's HTTP_REQUEST action performs: the rule carries the request, and the run supplies the event its body template is rendered from. The call happens on a job with the webhook ladder's retries; the answer is a JobRef to poll.

Request body application/json — HttpRequestCall

NameTypeRequiredDescription
methodstring
  • GET
  • POST
  • PUT
  • PATCH
  • DELETE
required
urlstring requiredThe address, http or https.
headersobject optionalPlain headers - a content type, an API version.
secret_header_namestring optionalThe header the secret travels in, e.g.
secret_header_valuestring optionalThe secret itself.
signature_headerstring optionalWhen set, this header carries an HMAC-SHA256 over the body computed with the secret - the same t=<ts>,v1=<hex> shape a webhook signature has.
body_templatestring optionalA CEL expression producing the body, rendered against the run's event when the call is made.
event_idstring (uuid) optionalThe event the body template reads.

Responses

  • 202 The call was accepted JobRef
  • 4XX An error per RFC 9457 Problem
Example bash
curl -X POST "$HUBTASK_URL/integrations/http-requests" \  -H "Authorization: Bearer $HUBTASK_TOKEN" \  -H "Content-Type: application/json" \  -d '{  "method": "GET",  "url": "string",  "headers": {},  "secret_header_name": "string",  "secret_header_value": "string",  "signature_header": "string",  "body_template": "string",  "event_id": "018f6e2a-2b7c-7c1e-9b1a-3d5e6f7a8b9c"}'