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 secondsdescription(string, optional): Description of the signal eventname(string, optional): Name identifier for the signal
Note:
- Exactly one of
ip,asn, orcountrymust 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:
| Concern | v1 | v2 |
|---|---|---|
| Rule family | type (always access_rules) | kind: access_rule | waf_rule | smart_firewall_rule |
| Operation | action: block/unblock | op: upsert | delete |
| Enforcement verb | conflated into action | rule.action (per kind) |
| Identity | implicit (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_rule | block, challenge, ratelimit, allow | optional: request | response; auto-detected by Synapse from the expression when omitted |
smart_firewall_rule | block, allow | not 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:
- Discord: https://discord.com/invite/jzsW5Q6s9q
- Email: [email protected]