Create Rule Public Api

Create a rule in your organization.

One endpoint handles all supported rule_type values; the shape of content
must match the type (see request examples in this operation).

rule_typecontent formatNotes
atomic_sqlsql_query, optional entities, levelPer-event predicate.
scheduled_sqlquery, schedule, entities, optional throttleOrg predicate optional; named form.
signal_combinationquery, schedule, descriptionInternal portal org only.

Org scoping is applied per tenant at execution, so a query does not need its own
organization_id predicate. If you want one, use the named form —
organization_id = %(organization_id)s or nebulock_organization_id = %(organization_id)s
— which the engine resolves at render time. The positional form (organization_id = ?) is
rejected on save: ClickHouse has no positional binding, so it cannot be parsed. Existing
rules that still use it are only warned about.

Organization ID: Omit organization_id to create under your session organization.
If provided, it must match your session — cross-tenant creates are rejected.
Global rules are created by signing in as NEBULOCK_INTERNAL_PORTAL_ORG_ID;
the legacy "*" marker is not accepted.

Status: New rules must be inactive (the default) — active and experimental
are rejected on create. A rule goes live only after a retro run proves it executes:
create it, run POST /rules/{rule_id}/runs, then PATCH /rules/{rule_id}/set-status.

Validation failures return 400 with error.message = "Rule validation failed",
the reasons in error.validation_errors and error.field_validations, and
error.debug holding the diagnostics behind them (engine payload, the SQL that was
checked, a ClickHouse backtrace). Fix against the first; quote the second to us.
error.message_markdown is the same error list rendered as markdown bullets, for
showing the failure without building the list yourself.

Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…
Body Params

The one concrete create model. organization_id is optional because
create_rule always overwrites it with the session org — a caller cannot
attribute a create to another tenant.

string
enum
required

Supported rule types (signal generators).

NOTE: signal-api's Postgres ruletype enum still carries labels this does not
name (SIGMA, SQL, STREAMING_SQL, CORRELATION). They are left in
place deliberately — dropping a label needs a full type rebuild — and excluded from
alembic comparison.

A stored row carrying one does not load, which is intended: no code path can
execute it. But it must not stop the rows around it loading either, and that does
not come for free — SQLAlchemy's Enum raises from its result processor, which
fails the whole SELECT rather than the one row. signal-api reads this column
through TolerantRuleTypeEnum (signal_api.models.database) for that reason.
Anything else that maps this enum onto a stored column needs the same care.

Allowed:
content
required

Rule title (will be populated from content if not provided)

Rule description (will be populated from content if not provided)

enum

Rule status


Rule status. See signal-api's RULE_LIFECYCLE.md for the transition diagram.

0 to 100

Rule severity (0-100)

tags
array of strings

Rule tags

Tags
array | null

Detection categories. None = caller omitted the field; the service layer coerces to [] before persisting so the DB never stores NULL.

array | null

Data sources. None = caller omitted the field; the service layer coerces to [] before persisting so the DB never stores NULL.

User ID

object | null

Additional rule details

Origin of the rule

ID of the hunt or rule-agent run the rule was created from

enum

What created the rule: hunt, api, manual, or rule-agent


What produced a rule, as opposed to origin which records the surface it arrived on.

Organization that owned the creating reference (auto-detections only)

Organization ID

Headers
string
required

Your API Key ID

string

Your API Key Secret

Responses

Language
Credentials
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
application/json