Skip to main content
CID222 Docs

Send your first request

Issue a gateway API key, add a provider credential, and make a chat request that reads the Server-Sent Events stream correctly.

  • Version: 0.4
  • Role: admin_user
  • Type: task

Get a prompt through CID222 to a provider and back. You create a gateway API key, give CID222 a provider credential to spend, and make one request whose reply you read as a Server-Sent Events (SSE) stream.

What do I need?

Licence
Any
Role
admin_user

Prerequisites

  • The appliance has completed the first-boot setup wizard and serves the dashboard.
  • You can sign in to the dashboard with a tenant that has the admin_user role.
  • You hold an API key for at least one LLM provider (OpenAI, Azure OpenAI, Anthropic or Google).
  • Your client can reach the gateway on TCP 443, or on TCP 3000 if you are calling the container directly.
  • curl 7.72 or later, or Python 3.9 or later with the requests package, is installed on the machine you test from.

CID222 returns text/event-stream from POST /chat/completions on every call. There is no non-streaming mode, so the examples below read the body line by line rather than parsing it as JSON.

Make the first call

Create a gateway API key

Select Administration → API Keys, then select Create API key. Give the key a name and, optionally, an expiry date.

The key is displayed once, in the form cid_key_ followed by 64 hexadecimal characters. CID222 stores only its hash and cannot show it again.

Store the key in your environment

Copy the key into an environment variable on the machine you will call from.

export CID222_API_KEY="cid_key_0123456789abcdef…"
export CID222_BASE_URL="https://<appliance-fqdn>"

echo "$CID222_API_KEY" prints a value beginning cid_key_.

Add a provider credential

Select Administration → Credentials, then select Add credential. Choose the provider, paste the provider's own API key, and save.

The credential appears in the list with its provider and scope. CID222 encrypts the value and never returns it.

List the models your tenant can use

Call GET /models with the gateway API key.

curl -sS "$CID222_BASE_URL/models" \
  -H "Authorization: Bearer $CID222_API_KEY"

The response is a JSON array. Only models whose provider has an active credential for your tenant appear, so an empty array means the previous step did not take effect.

[
  {
    "id": "0f2a…",
    "model_name": "gpt-4o",
    "display_name": "GPT-4o (Vision)",
    "provider": { "id": "8c31…", "code": "openai", "name": "OpenAI" },
    "supports_vision": true,
    "supports_streaming": true,
    "is_active": true,
    "created_at": "2026-09-01T08:12:44.000Z"
  }
]

Send a prompt that contains PII

Post one message to POST /chat/completions and read the stream. Pass provider as well as model whenever the model name is offered by more than one provider — gpt-4o is registered by both openai and azure_openai.

curl -N -sS -X POST "$CID222_BASE_URL/chat/completions" \
  -H "Authorization: Bearer $CID222_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o",
    "provider": "openai",
    "messages": [
      {
        "role": "user",
        "content": "Summarise this feedback in one sentence: Great product. Reach me at john.smith@example.com or 555-0142."
      }
    ]
  }'

-N disables curl's output buffering. Without it the events appear only when the stream closes.

The stream pauses for the length of the generation, then delivers the complete reply in one event:

data: {"type":"token_usage","usage":{"prompt_tokens":41,"completion_tokens":19,"total_tokens":60}}

data: {"id":"filtered-response","content":"The customer is happy with the product and left contact details.","finish_reason":"stop","filtered":false,"entities_masked":0}

data: [DONE]

Warning

The reply arrives as one event, not as per-token deltas, and the event names are CID222's own. A client written against the OpenAI streaming format finds no choices, no delta, and no incremental text. Read Chat API before wiring an existing SDK to this endpoint.

Verify

  1. The command from the last step ends with data: [DONE], and the line before it carries "id":"filtered-response" with a non-empty content.
  2. Select Detection & Filtering → All Detections. A row exists for the request, showing the email address and phone number with their entity types, confidences and the action taken.
  3. Repeat the request with an obviously toxic or jailbreak prompt. The stream carries a single event with an error field and "type":"content_rejected", then data: [DONE], and the provider is never called.

If it fails

  • GET /models returns [] → the tenant has no active credential for any provider. Repeat the credential step, and check the credential is scoped to your tenant or to a group your tenant belongs to.
  • The request returns 401 → the header is malformed. It must be Authorization: Bearer cid_key_<64 hex>, not an X-API-Key header.
  • The request returns 402 with LICENSE_EXPIRED → the installed licence is invalid or expired. An administrator installs a renewal under Settings → License.
  • The request returns 403 with ROLE_NOT_FOR_CHAT → the calling tenant has the auditor role, which does not use chat.
  • Nothing appears for a minute and then everything appears at once → this is the buffered stream behaving as designed, not a fault.

Next steps

  • Authentication — when to use a JWT instead of an API key, and which surfaces refuse API keys.
  • Chat API — every request field and every event the stream can carry.
  • Sessions API — let CID222 keep the conversation history instead of resending it.

Last updated on

On this page

Download PDF