Skip to main content

Signal API

The Signal API allows you to submit security events and signals for threat detection and analysis.

Base URL

https://api.gen0sec.com

Authentication

All API requests require authentication using a Bearer token in the Authorization header:

Authorization: Bearer <your-api-key>

Endpoints

Submit Signal Event

Submit a security signal event for processing.

Endpoint: POST /v1/signal

Request Body:

The endpoint accepts a batch of signal events as an array. You can send one or multiple events in a single request (up to 1000 events per batch).

Single Event Examples:

Block by IP address:

[
{
"type": "access_rules",
"action": "block",
"ip": "192.168.1.100",
"expiration": 3600,
"description": "Blocked due to suspicious activity",
"name": "suspicious-ip-block"
}
]

Block by ASN:

[
{
"type": "access_rules",
"action": "block",
"asn": "AS12345",
"expiration": 3600,
"description": "Blocked ASN due to malicious activity",
"name": "suspicious-asn-block"
}
]

Block by Country:

[
{
"type": "access_rules",
"action": "block",
"country": "US",
"expiration": 3600,
"description": "Blocked country due to threat intelligence",
"name": "suspicious-country-block"
}
]

Batch Example (Multiple Events):

[
{
"type": "access_rules",
"action": "block",
"ip": "192.168.1.100",
"description": "Blocked IP"
},
{
"type": "access_rules",
"action": "block",
"asn": "AS12345",
"description": "Blocked ASN"
},
{
"type": "access_rules",
"action": "block",
"country": "US",
"description": "Blocked country"
}
]

Parameters:

  • type (string, required): Event type. Currently supports: "access_rules"
  • action (string, required): Action to take. Valid values: "block" or "unblock"
  • ip (string, optional): IP address to block/unblock (IPv4 or IPv6, CIDR notation supported)
  • asn (string, optional): ASN to block/unblock (format: AS12345, must exist in database)
  • country (string, optional): Country code to block/unblock (ISO-3166 Alpha-2 format, e.g., US, GB, JP)
  • expiration (integer, optional): Expiration time in seconds
  • description (string, optional): Description of the signal event
  • name (string, optional): Name identifier for the signal

Note:

  • Exactly one of ip, asn, or country must be provided and non-empty per event. You cannot provide multiple fields simultaneously in a single event.
  • The endpoint accepts a batch of events (array format). Maximum batch size is 1000 events per request.

Response:

The endpoint processes events in batch and returns a summary of the processing results.

Success (200) - All events processed successfully:

{
"success": true,
"message": "Processed 3 entries, 0 failed"
}

Partial Success (206) - Some events failed:

{
"success": false,
"message": "Processed 2 entries, 1 failed",
"errors": [
"Entry 1: Schema validation failed: country: Country must be a valid ISO-3166 Alpha-2 code (e.g., US, GB, JP)"
]
}

Error (400):

{
"error": "ValidationError",
"message": "Validation failed",
"details": [
{
"field": "action",
"message": "must be one of: block, unblock",
"value": "invalid"
}
],
"code": 400
}

Error (401):

{
"error": "Unauthorized",
"message": "Invalid or missing API key",
"code": 401
}

Signal API v2

POST /v1/signal (the flat access_rules shape above) is unchanged and remains supported. v2 adds POST /v2/signal, a typed envelope that also supports WAF and Smart Firewall rules and can be consumed by Synapse.

Why v2

The v1 body flattened rule fields to the top level and overloaded action (block/unblock) to mean both what to do with the rule and the rule's enforcement verb. v2 separates three concerns:

Concernv1v2
Rule familytype (always access_rules)kind: access_rule | waf_rule | smart_firewall_rule
Operationaction: block/unblockop: upsert | delete
Enforcement verbconflated into actionrule.action (per kind)
Identityimplicit (by value)rule_ref (caller-stable id)

Endpoint

Endpoint: POST /v2/signal — Bearer auth, JSON array, up to 1000 envelopes per batch. Per-entry results: 200 if all succeed, 206 if any entry fails (the response lists per-entry errors), identical to v1.

Envelope

{
"schema_version": 2, // required (defaulted to 2 on /v2/signal)
"kind": "waf_rule", // access_rule | waf_rule | smart_firewall_rule
"op": "upsert", // upsert | delete
"rule_ref": "soar-pb12-sqli-login", // caller-stable id; REQUIRED for waf/smart_firewall
"expires_in": 86400, // seconds; 0/omitted = no expiry; ignored on delete
"reason": "SOAR playbook PB-12 matched SQLi",
"labels": { "playbook": "PB-12" }, // optional structured metadata
"rule": { /* kind-specific; omit when op=delete */ }
}

rule_ref is the upsert/delete key and the idempotency key: re-sending the same rule_ref updates the existing active rule rather than creating a duplicate. It must match ^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$.

Access rule (kind: access_rule)

Exactly one of ip, asn, country in target. op: delete replaces v1's action: "unblock". expires_in replaces v1 expiration (same policy: a block with no expiry defaults to one day, any positive value is floored at 60s). rule_ref is optional here — without it, v1-style match-by-value applies.

[
{
"schema_version": 2,
"kind": "access_rule",
"op": "upsert",
"rule_ref": "soar-block-203-0-113-10",
"expires_in": 3600,
"reason": "Brute force from this host",
"rule": { "target": { "ip": "203.0.113.10/32" }, "action": "block" }
}
]

Remove it later:

[
{ "schema_version": 2, "kind": "access_rule", "op": "delete",
"rule_ref": "soar-block-203-0-113-10" }
]

WAF rule (kind: waf_rule) and Smart Firewall rule (kind: smart_firewall_rule)

Both use the same expression-rule body. expression is a Wirefilter expression and is syntactically validated at ingest by the rules-validator service against the scheme for that kind (WAF = HTTP L7 fields; Smart Firewall = L3/L4 + JA4 fields, no http.*); an unparseable expression — or a WAF field used in a Smart Firewall rule — rejects that entry with a 206 and a precise error. rule_ref is required.

Action and phase are kind-specific, matching the Synapse enforcement engine:

action (allowed values)phase
waf_ruleblock, challenge, ratelimit, allowoptional: request | response; auto-detected by Synapse from the expression when omitted
smart_firewall_ruleblock, allownot allowed (L3/L4 is pre-connection)

phase is carried through into waf_rules.config (under the phase key) — there is no separate column. action and phase are matched case-insensitively and stored lowercase.

[
{
"schema_version": 2,
"kind": "waf_rule",
"op": "upsert",
"rule_ref": "soar-pb12-sqli-login",
"reason": "SOAR playbook PB-12 matched SQLi",
"rule": {
"name": "Block SQLi on /login",
"expression": "http.request.uri.path eq \"/login\" and http.request.body.raw contains \"union select\"",
"action": "block",
"phase": "request",
"order": 100,
"short_description": "SQLi guard",
"config": { "sensitivity": "high" }
}
}
]

smart_firewall_rule uses the same body shape, but with action limited to block/allow, no phase, and an L3/L4 + JA4 expression (e.g. ip.src in 192.0.2.0/24 and ja4t == "64240_2-4-8-1-3_1460_10") — http.* fields are rejected for this kind.

Response

Same structure as v1:

{ "success": true, "message": "Processed 1 entries, 0 failed", "errors": [] }

A failed entry (e.g. bad expression, missing rule_ref, invalid target) yields HTTP 206 with the per-entry reason in errors.

Backward compatibility

/v1/signal, its body shape, and its Kafka format are untouched — existing Synapse and SOAR integrations are unaffected. v2 flows on the same Kafka topic with a schema_version header; the consumer routes v1 (absent/1) to the legacy access-rule path and v2 to the typed path. WAF / Smart Firewall upserts land in waf_rules / smart_firewall_rules; access rules continue to land in access_rules.

Interactive Documentation

Interactive API documentation is available at:

https://api.gen0sec.com/docs/signal/swagger/

Example Usage

Block by IP Address

curl -X POST https://api.gen0sec.com/v1/signal \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '[
{
"type": "access_rules",
"action": "block",
"ip": "192.168.1.100",
"description": "Blocked due to suspicious activity"
}
]'

Block by ASN

curl -X POST https://api.gen0sec.com/v1/signal \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '[
{
"type": "access_rules",
"action": "block",
"asn": "AS12345",
"description": "Blocked ASN due to malicious activity"
}
]'

Block by Country

curl -X POST https://api.gen0sec.com/v1/signal \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '[
{
"type": "access_rules",
"action": "block",
"country": "US",
"description": "Blocked country due to threat intelligence"
}
]'

Batch Request (Multiple Events)

curl -X POST https://api.gen0sec.com/v1/signal \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '[
{
"type": "access_rules",
"action": "block",
"ip": "192.168.1.100",
"description": "Blocked IP"
},
{
"type": "access_rules",
"action": "block",
"asn": "AS12345",
"description": "Blocked ASN"
},
{
"type": "access_rules",
"action": "block",
"country": "US",
"description": "Blocked country"
}
]'

Python

import requests

url = "https://api.gen0sec.com/v1/signal"
headers = {
"Authorization": "Bearer your-api-key",
"Content-Type": "application/json"
}

# Block by IP (single event in batch)
data = [
{
"type": "access_rules",
"action": "block",
"ip": "192.168.1.100",
"description": "Blocked due to suspicious activity"
}
]

# Block by ASN (single event in batch)
data = [
{
"type": "access_rules",
"action": "block",
"asn": "AS12345",
"description": "Blocked ASN due to malicious activity"
}
]

# Block by Country (single event in batch)
data = [
{
"type": "access_rules",
"action": "block",
"country": "US",
"description": "Blocked country due to threat intelligence"
}
]

# Batch request (multiple events)
data = [
{
"type": "access_rules",
"action": "block",
"ip": "192.168.1.100",
"description": "Blocked IP"
},
{
"type": "access_rules",
"action": "block",
"asn": "AS12345",
"description": "Blocked ASN"
},
{
"type": "access_rules",
"action": "block",
"country": "US",
"description": "Blocked country"
}
]

response = requests.post(url, json=data, headers=headers)
print(response.json())

Go

package main

import (
"bytes"
"encoding/json"
"net/http"
)

func main() {
url := "https://api.gen0sec.com/v1/signal"

// Block by IP (single event in batch)
data := []map[string]interface{}{
{
"type": "access_rules",
"action": "block",
"ip": "192.168.1.100",
"description": "Blocked due to suspicious activity",
},
}

// Block by ASN (single event in batch)
data = []map[string]interface{}{
{
"type": "access_rules",
"action": "block",
"asn": "AS12345",
"description": "Blocked ASN due to malicious activity",
},
}

// Block by Country (single event in batch)
data = []map[string]interface{}{
{
"type": "access_rules",
"action": "block",
"country": "US",
"description": "Blocked country due to threat intelligence",
},
}

// Batch request (multiple events)
data = []map[string]interface{}{
{
"type": "access_rules",
"action": "block",
"ip": "192.168.1.100",
"description": "Blocked IP",
},
{
"type": "access_rules",
"action": "block",
"asn": "AS12345",
"description": "Blocked ASN",
},
{
"type": "access_rules",
"action": "block",
"country": "US",
"description": "Blocked country",
},
}

jsonData, _ := json.Marshal(data)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", "Bearer your-api-key")
req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
}

Rate Limits

API rate limits apply to prevent abuse. Contact support if you need higher limits.

Support

For API support, visit: