Skip to content
Console
Browse documentation
Guide

Integrate without an SDK

Send product events and read feature flags from any server over HTTP: two addresses, one key per purpose, stable IDs, envelope-preserving retries and an independent exposure.

On this page

Any server can integrate feature flags and product events with plain HTTP, whether its language has no Anectico SDK, you do not want one, or the SDK path does not cover it yet. Your agent writes the calls in your repository and runs your application. Anectico supplies the routes and the evidence reads.

One recipe was tested: a small checkout service in Ruby 2.6.10 using only its standard library. Other languages follow the same calls, but only this one was run, so say so when you hand it back. The code is in Runnable integration examples.

Addresses and keys by purpose

Each address and key has one job. A key that reads cannot send, and a key that sends cannot read.

Purpose Address Credential Route
Flag decisions The control address, for example https://app.anectico.com A project-bound management key with only flags:read, in server secret configuration POST /api/v1/decide
Events, identify, account and flag exposures The capture address, for example https://api.anectico.com A server_capture key with only analytics:write POST /api/v1/capture
Existing OpenTelemetry telemetry The capture address A native_ingest key with only ingest:write /v1/traces, /v1/logs, /v1/metrics
Verification and the agent's own work The control address The agent's management key, never held by the application POST /api/v1/instrumentation/verify

Send a key in X-Anectico-API-Key; X-API-Key and Authorization: ApiKey are also accepted. Read both addresses and both keys from the application's configuration. Do not default an address, do not fall back from one key to another, and refuse to start when a setting is missing or both keys are the same string. The flags key reads every flag definition of the project, so it never goes to a browser, a mobile app, a log or a repository. See Native intake keys.

Send an event

Choose the message_id and the timestamp before the first send, derived from something your test controls. Put the account in the event's $groups. The identity comes from your server's own session, never from the request body.

{
  "events": [
    {
      "event": "checkout_completed",
      "distinct_id": "user_8842",
      "session_id": "sess_7d1c",
      "message_id": "run-42:checkout_completed",
      "timestamp": "2026-10-15T09:30:12.123Z",
      "properties": { "cart_value": 129, "$groups": { "company": "acme-corp" } }
    }
  ]
}
curl --fail-with-body -X POST "$ANECTICO_CAPTURE_URL/api/v1/capture" \
  -H "X-Anectico-API-Key: $ANECTICO_CAPTURE_API_KEY" \
  -H "Content-Type: application/json" \
  --data @event.json

session_id is optional; send it when your application owns the browsing session. Identifiers are printable and at most 200 bytes.

Moment Event
Login {"event": "identify", "distinct_id": "user_8842", "anon_distinct_id": "visitor_19", "set": {"plan": "pro"}, "message_id": ..., "timestamp": ...} links the visitor's earlier events to the person and updates the profile
Account {"event": "$groupidentify", "distinct_id": "user_8842", "group_type": "company", "group_key": "acme-corp", "message_id": ..., "timestamp": ...} records the account
Profile fields set, set_once and unset on an event update the canonical customer; null in set is a value, not a deletion

identify and $groupidentify store no event row, so no verification can select them: their outcome is the acknowledgement. See Direct customer-property updates and Identify customers.

Read the acknowledgement, and retry safely

HTTP 200 means a batch ledger came back, not that the event is stored. Read every item:

Item Meaning Action
CAPTURE_DELIVERY_QUEUED Accepted for processing; not proof of storage or of a finished identity update Never retry
CAPTURE_DELIVERY_REJECTED Refused (INVALID_EVENT or QUOTA_EXCEEDED) Final for that input; fix it, do not loop
CAPTURE_DELIVERY_UNKNOWN, HTTP 429 or 503, a timeout or a broken connection Uncertain Back off and resend the same bytes, with the same message ID, a bounded number of times

Serialize the body once and resend those bytes. Reusing a message ID with different content refuses that event, so never rebuild an event for a retry. A 2xx with a body you cannot read is uncertain, not delivered. See Capture acknowledgements.

Flags: decide, serve, then record the exposure

  1. POST /api/v1/decide to the control address with the flags key, the person's distinct_id and the explicit person_properties the flag's rules use. The answer carries feature_flags, project_id, feature_flag_versions and, per flag, feature_flag_results with the evaluation status, whether the value is a fallback, and the input context.
  2. Serve the branch from the value. false from a successful decision is a value. A failed request, a non-200 answer or a missing flag is an unavailable decision: serve the safe default and send no exposure.
  3. Independently, send a $feature_flag_called event to the capture address with the capture key. Copy the decision's own fields into the reserved properties, never compute a version, never write an unknown fallback as false, and leave absent context fields absent. The properties and the input binding the application computes are on Feature-flag exposure verification.

Local evaluation from the snapshot is not covered over plain HTTP: it needs the SDK's frozen bucketing and comparison rules.

Existing OpenTelemetry telemetry

If you already export OTLP, keep it: point the exporter at the capture address with a native_ingest key and set anectico.distinct_id or trusted W3C baggage at a boundary your server controls. See OpenTelemetry. An OTLP attribute does not create or link an identity: that is the identify event above. Checking traces and logs with the exact verification reads is not covered yet.

Prove it

The agent selects the samples whose delivery was queued and asks for the product_events and flags jobs, as in Integration job verification. What a verified sample does and does not establish, and the limits, are on What integration verification proves and what it does not.

Other jobs over HTTP, and what is not offered

Job Where it is documented Part of this verified workflow
Survey delivery Surveys Not yet
Experiment delivery Product experiments Not yet
Browser session replay behind a backend in any language Record session replay safely Not yet
Native structural replay iOS, Android, React Native and Flutter setup Outside these server examples; use the platform recorder or adapter
Replay over OTLP Not offered No

Browser replay uses the JavaScript recorder. Native structural replay has its own format, consent, privacy policy and upload credential. OTLP carries neither recording format.

Next