Ingestion API

Trigger

Fan out a payload to every endpoint subscribed to an event type

Trigger an event type — Nahook fans out a single payload to every active endpoint subscribed to that event type in the API key's workspace (and environment, if the key is environment-scoped). Subscriptions are managed via the Management API or the dashboard.

POST /api/ingest/event/{eventType}

Request

curl -X POST https://us.api.nahook.com/api/ingest/event/order.paid \
  -H "Authorization: Bearer nhk_us_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "payload": { "orderId": "ord_123", "status": "paid" },
    "metadata": { "source": "checkout-service" }
  }'
const result = await nahook.trigger("order.paid", {
  payload: { orderId: "ord_123", status: "paid" },
  metadata: { source: "checkout-service" }, // optional
});
result = client.trigger("order.paid", {
    "orderId": "ord_123",
    "status": "paid",
}, metadata={"source": "checkout-service"})  # optional
result, err := c.Trigger(ctx, "order.paid", nahook.TriggerOptions{
    Payload:  map[string]any{"orderId": "ord_123", "status": "paid"},
    Metadata: map[string]string{"source": "checkout-service"}, // optional
})
import com.nahook.types.TriggerOptions;
import java.util.Map;

var payload = Map.of("orderId", "ord_123", "status", "paid");
var metadata = Map.of("source", "checkout-service");
TriggerResult result = client.trigger("order.paid",
    new TriggerOptions(payload, metadata)); // metadata optional
var result = await client.TriggerAsync("order.paid", new TriggerOptions {
    Payload = new Dictionary<string, object> {
        ["orderId"] = "ord_123",
        ["status"] = "paid"
    },
    Metadata = new Dictionary<string, string> { ["source"] = "checkout-service" } // optional
});
$result = $client->trigger("order.paid", [
    "payload" => ["orderId" => "ord_123", "status" => "paid"],
    "metadata" => ["source" => "checkout-service"], // optional
]);
result = client.trigger("order.paid",
  payload: { order_id: "ord_123", status: "paid" },
  metadata: { "source" => "checkout-service" }) # optional
use std::collections::HashMap;

let result = client.trigger("order.paid", TriggerOptions {
    payload: serde_json::json!({
        "orderId": "ord_123",
        "status": "paid"
    }),
    metadata: Some(HashMap::from([
        ("source".into(), "checkout-service".into()),
    ])), // optional
}).await?;

Body

FieldTypeRequiredNotes
payloadobjectyesArbitrary JSON. Each subscribed endpoint receives the same payload.
metadataobject<string, string>noUp to 5 keys. Values must be strings. Attached to every fanned-out delivery for filtering / debugging.

The API key must be workspace-scoped (not endpoint-scoped) — fan-out needs to enumerate subscriptions across the workspace. A 400 is returned otherwise.

Response

202 Accepted

{
  "eventTypeId": "evt_order_paid",
  "deliveryIds": ["del_a1", "del_b2", "del_c3"],
  "status": "accepted",
  "failures": []
}
FieldTypeNotes
eventTypeIdstringInternal ID of the matched event type.
deliveryIdsstring[]One entry per endpoint that was successfully queued.
statusstringAlways "accepted" on a 202 — even partial fan-outs. See failures.
failuresarrayEmpty when every subscribed endpoint was enqueued. Populated on partial fan-out (see below).

Partial fan-out

If at least one subscribed endpoint was queued, Nahook returns 202 even when others couldn't be enqueued (e.g., FK violation, statement timeout). The failures array describes each one. Successfully-queued siblings have still been published.

{
  "eventTypeId": "evt_order_paid",
  "deliveryIds": ["del_a1"],
  "status": "accepted",
  "failures": [
    { "endpointId": "ep_xyz", "code": "internal_error", "message": "Failed to publish to Kafka" }
  ]
}

If no endpoint was queued (no subscriptions, every endpoint failed, etc.), deliveryIds is empty and the status code is still 202. Check whether deliveryIds is empty to detect "nothing was delivered."

Errors

StatusCodeWhen
400invalid_api_keyUsed a key that isn't workspace-scoped. Fan-out requires workspace access.
401unauthorizedMissing, malformed, or revoked API key.
413payload_too_largePayload exceeded your plan's per-payload cap.
429rate_limitedWorkspace or per-API-key rate limit hit.
504ingest_timeoutServer-side ingest exceeded 5 s. Safe to retry.

trigger does not accept an idempotencyKey — duplicate calls produce duplicate deliveries. If you need dedup semantics across fan-outs, do it on your side before calling, or use Send against a specific endpoint instead.