ASTROWARE
EU Mode
EU AI Act Risk ScreeningEU AI Act testPII checker
Chaos Mode, Planned
HaloGuard EU, LivePrivacy filter, LiveAuto Mode, LiveHaloGuard 1.0, Open weights (opens in a new tab)Security checks, In developmentConsumer-law checks, PlannedMulti-modal checks, PlannedWhat we are building
PricingDocsResearch
Sign inBook a callStart free
Docs · EU Mode

EU Mode API

Send EU Mode a request on its way in, a reply on its way out, or the actions your agent is about to take. It answers allow, mask, escalate or block. Your app routes on that answer. We never hold your model key and never call your model.

Start with a $5 trial budget
  • Until our data processing agreement is signed with your company, send no personal data to the API.
  • During the trial there is no uptime guarantee: the guard can be briefly unavailable.
Start free
On this page1 · Before you start2 · The call3 · The answer4 · Shadow or enforce5 · Errors6 · Limits and price7 · Your data8 · Not promised

1Before you start

  • ·Sign in to the console to create a key. The key is shown once. You can revoke or restore it there. A key that calls HaloGuard EU can also call EU Mode.
  • ·Call the API from your server. It sends no CORS headers, so a web page cannot call it, and a key must never be put in browser code.
  • ·Every key starts in shadow mode (section 4). Switch a key to enforce on the API keys page in the console when you are ready.

2The call

POST https://api.astroware.ai/v1/chat/completions. The same endpoint, key and price as HaloGuard EU; only the model name differs.

Headers
Authorization: Bearer <key> and Content-Type: application/json
model
"eu-mode". Required.
messages
A list of messages. The last one decides what is checked (table below).
The last messagecheckedWhat runs
A user messagerequest (on its way in to your model)AI Act check and personal-data check
An assistant message with textreply (on its way out of your model)AI Act check
An assistant message with tool_calls (OpenAI shape, up to 20 calls)action (what your agent is about to do)Auto Mode, the dangerous action check
  • ·Requests and replies: the same rules as HaloGuard EU. Only user and assistant messages, text only. A tool or system message gives 400. Up to about 7,800 tokens in a conversation.
  • ·Actions: the user messages are what the person asked for, and earlier tool calls give the context. tool messages (tool results) and a system message are accepted and not used. Up to 400,000 characters in the conversation, and at most 20 tool calls in the last message.
  • ·For all three: a body of up to 2 MB, no streaming.
ShellExample call
curl https://api.astroware.ai/v1/chat/completions \
  -H "Authorization: Bearer $HALOGUARD_KEY" -H "Content-Type: application/json" \
  -d '{"model": "eu-mode", "messages": [
        {"role": "user", "content": "Write to Anna Schmidt at anna.schmidt@example.com about the Q3 report."}]}'

3The answer

Status 200 and an OpenAI-style chat completion. choices[0].message.content is itself a JSON string. Parse it. These three were recorded on the live API on 30 October 2026, with a key in shadow mode.

A request with a name and an email address:

JSONRequest body
{
  "model": "eu-mode",
  "messages": [
    {
      "role": "user",
      "content": "Write to Anna Schmidt at anna.schmidt@example.com about the Q3 report."
    }
  ]
}
JSONRecorded on the live API, 30 October 2026
{
  "id": "hg-b1a320fe4cca4ae78b339bc90d89c1df",
  "created": 1793333504,
  "model": "eu-mode",
  "object": "chat.completion",
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "message": {
        "content": "{\"decision\": \"mask\", \"mode\": \"shadow\", \"would_decide\": \"mask\", \"checked\": \"request\", \"checks\": [{\"check\": \"ai_act\", \"decision\": \"allow\", \"policy\": \"none\"}, {\"check\": \"privacy\", \"decision\": \"mask\", \"personal_data\": [\"email\", \"person\"], \"masked_messages\": [{\"role\": \"user\", \"content\": \"Write to [PERSON_1] at [EMAIL_1] about the Q3 report.\"}], \"map\": {\"[PERSON_1]\": \"Anna Schmidt\", \"[EMAIL_1]\": \"anna.schmidt@example.com\"}, \"privacy_mode\": \"all\"}]}",
        "role": "assistant",
        "provider_specific_fields": {
          "refusal": null
        }
      },
      "provider_specific_fields": {}
    }
  ],
  "usage": {
    "completion_tokens": 8,
    "prompt_tokens": 24,
    "total_tokens": 32,
    "haloguard_eu_mode": {
      "checked": "request",
      "decision": "mask",
      "would_decide": "mask",
      "mode": "shadow"
    }
  }
}

A reply:

JSONRequest body
{
  "model": "eu-mode",
  "messages": [
    {
      "role": "user",
      "content": "Summarise our Q3 report."
    },
    {
      "role": "assistant",
      "content": "Revenue rose 4 percent in Q3; costs were flat."
    }
  ]
}
JSONRecorded on the live API, 30 October 2026
{
  "id": "hg-306e6c0431c64c3fb9ad25fb45355321",
  "created": 1793333344,
  "model": "eu-mode",
  "object": "chat.completion",
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "message": {
        "content": "{\"decision\": \"allow\", \"mode\": \"shadow\", \"would_decide\": \"allow\", \"checked\": \"reply\", \"checks\": [{\"check\": \"ai_act\", \"decision\": \"allow\", \"policy\": \"none\"}], \"skipped\": [{\"check\": \"privacy\", \"reason\": \"reply\"}]}",
        "role": "assistant",
        "provider_specific_fields": {
          "refusal": null
        }
      },
      "provider_specific_fields": {}
    }
  ],
  "usage": {
    "completion_tokens": 8,
    "prompt_tokens": 30,
    "total_tokens": 38,
    "haloguard_eu_mode": {
      "checked": "reply",
      "decision": "allow",
      "would_decide": "allow",
      "mode": "shadow"
    }
  }
}

An action (the agent is about to send the email the person asked for):

JSONRequest body
{
  "model": "eu-mode",
  "messages": [
    {
      "role": "user",
      "content": "Email the Q3 summary to anna.schmidt@example.com"
    },
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "call_1",
          "type": "function",
          "function": {
            "name": "send_email",
            "arguments": "{\"to\": \"anna.schmidt@example.com\", \"subject\": \"Q3 summary\", \"body\": \"Revenue rose 4 percent in Q3.\"}"
          }
        }
      ]
    }
  ]
}
JSONRecorded on the live API, 30 October 2026
{
  "id": "hg-87f256ead4024dcebcbe4ef6e7b86be4",
  "created": 1793333506,
  "model": "eu-mode",
  "object": "chat.completion",
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "message": {
        "content": "{\"decision\": \"allow\", \"mode\": \"shadow\", \"would_decide\": \"allow\", \"checked\": \"action\", \"checks\": [{\"check\": \"action\", \"decision\": \"allow\", \"engine\": \"auto-mode-v6\", \"calls\": [{\"tool\": \"send_email\", \"decision\": \"allow\", \"band\": \"judged\", \"reason\": \"low risk, or asked for by the person\", \"p_yes\": 0.9248, \"threshold\": 0.5}]}], \"skipped\": [{\"check\": \"ai_act\", \"reason\": \"action\"}, {\"check\": \"privacy\", \"reason\": \"action\"}]}",
        "role": "assistant",
        "provider_specific_fields": {
          "refusal": null
        }
      },
      "provider_specific_fields": {}
    }
  ],
  "usage": {
    "completion_tokens": 0,
    "prompt_tokens": 54,
    "total_tokens": 54,
    "haloguard_eu_mode": {
      "checked": "action",
      "decision": "allow",
      "would_decide": "allow",
      "mode": "shadow",
      "judged": 1
    }
  }
}

decision is what your app routes on:

decisionWhat your app does
allowSend the text or run the action as it is.
maskSend masked_messages on instead. Putting the originals back into your model's reply with map is your step.
escalateHold it for a person to approve. You also get escalate when a check could not answer, so an outage never reads as a clean allow.
blockStop.

The most severe check decides: block, then escalate, then mask, then allow.

Each entry in checks has the check's own answer:

  • ·ai_act: decision allow or block, and policy (the policy that matched, or none). A block also carries label and act_area, as on the HaloGuard EU page.
  • ·privacy: decision allow or mask, personal_data (the kinds found), and on mask masked_messages and map. privacy_mode is your company's masking setting.
  • ·action: calls has one entry per tool call: tool, decision (allow, escalate or block) and reason. band, p_yes and threshold say how that decision was reached. They can change between versions; route on decision.
  • ·A check that could not answer says unavailable, and the answer is escalate.
  • ·A check that did not run is named in skipped, with the reason.

usage also carries haloguard_eu_mode: checked, decision, would_decide and mode, and for an action judged, the number of calls that were judged. It holds no text.

Ignore keys you do not know.

4Shadow or enforce

Each key has a mode. You set it on the API keys page in the console; a call cannot change it.

  • ·Shadow (every key starts here): an AI Act block and the action check's answer do not stop anything. decision lets the traffic through, and would_decide says what enforce would have answered. Your company's masking still applies in shadow: a request with personal data still answers mask. The Shadow report on the Usage page counts what would have been blocked or escalated.
  • ·Enforce: decision is what the checks found.

Write your routing once. Switching a key from shadow to enforce needs no change to your code, and takes effect on the key's next call.

5Errors

The body is {"error": {"message", "type", "param", "code"}}. Branch on the status, not on the message text: the text can change.

As on the HaloGuard EU page, with these differences:

StatusWhen
403A model name your key may not call (type is key_model_access_denied)
400For an action: more than 20 tool calls; tool_calls that is not a non-empty list; a tool call with no function.name; function.arguments that is not JSON, or not a JSON object; a role other than user, assistant, tool or system; a conversation over 400,000 characters

A check that could not run is not an error: the answer is 200 with decision escalate.

6Limits and price

These are today's settings and can change.

  • ·60 requests a minute per key.
  • ·Trial: the same $5 budget for your company as HaloGuard EU, shared by up to 5 active keys.
  • ·Price: $30 / billion input tokens, counted on the messages you send. The answer is free. The same as HaloGuard EU.

7Your data

We see only what you send and keep none of its text. Usage records hold no text. For an action check, we send the tool call, with the earlier tool calls and the user messages in that request, to an AI provider in the EU, which judges whether the person asked for the action and keeps no copy. When the AI Act check is unsure about a message, we send that message, with earlier messages of the conversation, to the same provider, which decides and keeps no copy. Our Terms and our data processing agreement say how your data is processed, and by whom. Until our data processing agreement is signed with your company, send no personal data to the API.

8Not promised

No uptime guarantee: the guard can be briefly unavailable (a 503). Speed is not promised. The limits and budget above are today's settings and can change.

Auto Mode judges whether an action fits what the person asked for. It does not check what the tool returns. Security checks (prompt injection, jailbreaks) are in development and not in the eu-mode model yet.

Free toolsPII checkerEU AI Act Risk ScreeningEU AI Act test© 2026 AstroWorks FZE LLCPrivacyTermsTrustDocsBenchmarksEU AI Act dates for AI agentsContactHugging Face (opens in a new tab)