Hyperstruck
API

Agent configuration

Everything you can set on an agent, including tools, guardrails, approval gates, and what it pays attention to when learning.

An agent needs a name and core_config.instructions. Everything else on this page is optional, and most of it is how you turn a working agent into one you would let near production.

Three different strictness rules, on purpose

Top-level agent fields are rejected if unrecognized, so a typo or a retired field fails loudly instead of silently doing nothing. Top-level keys inside core_config are ignored if unrecognized, so an agent created against an older version keeps loading after the platform moves on; a misspelling there does not error, it just never takes effect. But the nested objects inside core_config are strict again: guardrails_config.pii, guardrails_config.prompt_injection and each entry in domain_dimensions all reject unknown keys. Deliberately so, since a caller still passing a removed enable flag would otherwise read as silently switching a safety control on.

The agent itself

These sit next to core_config, not inside it.

Prop

Type

There is no model field. Every reasoning component runs a model pinned after benchmarking for that component's specific job, with no silent substitution when one is unavailable.

Instructions

core_config.instructions is the agent's standing brief, and it is the one core_config field a create request must supply non-empty.

Write it as the operating context you would give a competent new hire: what this agent is responsible for, what it must never do, whose conventions apply. It is delivered to every reasoning component, not just the planner, so a standard you state here reaches the parts that plan, execute, and check the work.

What the agent pays attention to

core_config.domain_dimensions steers what an agent keeps when it learns. Each entry is a name, a description, and optional example values, and you can define up to 20.

{
  "domain_dimensions": [
    {
      "name": "reliability",
      "description": "Practices that keep services resilient under load and failure.",
      "examples": ["retries", "timeouts"]
    },
    {
      "name": "apply_phase",
      "description": "When in the delivery lifecycle the guidance applies.",
      "examples": ["design", "implementation", "operations"]
    }
  ]
}

Leave it out and the agent learns unguided, which is a reasonable default. Set it when an agent is doing narrow work and you want its lessons filed against the axes your team actually reasons in. On a patch, pass null or [] to clear.

Tools

core_config.mcp_servers attaches tool servers over MCP. Each entry needs a name and exactly one of url (HTTP or SSE transport) or command (stdio transport).

Prop

Type

Use auth_type with auth_token_env to reference a secret by name rather than embedding it, which is the hosted-safe form. Set allowed_tools when a server offers more than the agent should be able to reach; an agent cannot call a tool that was never registered.

Set category on anything dangerous

Use the category field above. A tool whose category is undeclared is treated as unresolved rather than harmless, so machinery that depends on knowing a tool is dangerous cannot protect you: a fact can never talk the agent out of a safeguard it does not know is a safeguard. See Grounded success.

Guardrails

core_config.guardrails_config holds two independent guardrails, pii and prompt_injection. Each is optional, and presence is the switch: leaving one unset is how you run without it. There is no enabled flag to get wrong.

Both run on the way in, before generation starts, which is the only point at which they can still help. They are separate from the grounding gate, which governs what a run may claim on the way out.

Guardrails are entitlement-gated

Configuring guardrails on a plan that does not include them returns a 403 rather than quietly ignoring the config. PII masking is an add-on for Pro and included for Enterprise. Check your plan before building around this section.

PII

{
  "guardrails_config": {
    "pii": {
      "entities": ["PERSON", "EMAIL_ADDRESS", "CREDIT_CARD"],
      "actions": { "PERSON": "anonymize", "CREDIT_CARD": "block", "SSN": "block" },
      "score_threshold": 0.5
    }
  }
}

Prop

Type

Each detected entity type takes one of three actions:

ActionWhat happens
anonymizeThe value is masked before the model sees it. The run continues.
blockThe content is refused outright rather than masked.
log_onlyDetected and recorded, nothing changed. Use when you want visibility before you want enforcement.

The defaults are deliberately not uniform, and worth knowing before you override them:

EntityDefault
CREDIT_CARD, SSNblock
PERSON, EMAIL_ADDRESS, PHONE_NUMBER, IP_ADDRESS, LOCATIONanonymize
ORGANIZATIONlog_only

Payment and identity numbers are blocked rather than masked, because a masked card number is still a card number that reached the boundary. Organization names are logged rather than masked, because masking them usually destroys the task.

Setting actions replaces the table above, it does not patch it

These defaults apply only when you omit actions entirely. The moment you supply the map, it is the whole map, and any entity missing from it falls back to anonymize. So a config that names only PERSON silently downgrades CREDIT_CARD and SSN from block to masking. If you override anything, restate every entity you want blocked.

Unknown keys are rejected here too

This config refuses fields it does not recognize. A caller still passing a removed enable flag would otherwise be read as silently switching a safety control on, arriving with no signal at all, so it fails loudly instead.

Prompt injection

prompt_injection screens for injection and jailbreak attempts using purpose-built classifiers rather than asking a model to police itself, which is the same reasoning behind the grounding gate not using an LLM judge.

Prop

Type

Switching this on starts refusing traffic

action defaults to block, so enabling this guardrail will refuse requests that previously passed. If you want to see what it would catch before it starts blocking, set action: "log_only" first and review the detections.

Approval gates

This is the part most worth setting deliberately, because the default is the permissive one.

Prop

Type

Setting hitl_enabled alone gives you no gates at all

This is the trap worth reading twice. hitl_enabled defaults to false, and when you turn it on the other defaults still apply: preset autonomy, level 5. Level 5 means no automatic gates, custom policies only. So hitl_enabled: true on its own installs zero policies, the request succeeds, and nothing ever pauses. There is no error to tell you.

To actually get gates you must also lower the autonomy level, or switch preset. Both working shapes are below.

All four of these live inside core_config, not next to it. Putting them at the top level is rejected outright, so this is a loud failure rather than a silent one:

{
  "name": "Collections Assistant",
  "core_config": {
    "instructions": "Chase overdue invoices and propose next actions.",
    "hitl_enabled": true,
    "hitl_autonomy_level": 2
  }
}

That agent pauses at plans, milestones, destructive and external tools, and escalations. The levels run 1 (every gate fires) to 5 (none), so pick the number by how much you want to be asked:

LevelWhat pauses
1Everything
2Plans, milestones, destructive and external tools, escalations
3Plans, low-confidence steps, destructive tools, escalations
4Destructive tools and escalations only
5Nothing automatic (the default)

Requiring more than one approver

A quorum needs two things set, not one. hitl_required_approvals above 1 requires hitl_enabled: true and hitl_policy_preset: "milestone_only"; anything else is rejected with a 422 rather than silently collapsing to a single approver.

{
  "core_config": {
    "instructions": "...",
    "hitl_enabled": true,
    "hitl_policy_preset": "milestone_only",
    "hitl_required_approvals": 2
  }
}

The details are what make the control worth anything:

  • Each approval must come from a distinct authenticated principal, meaning a distinct API key or portal login. That is not the same as a distinct human, so pair it with your own identity discipline.
  • The principal who dispatched the run is excluded from approving it.
  • Any single rejection vetoes, regardless of how many approvals were already collected.
  • One caller resuming twice replays rather than counting twice, so a double-click cannot manufacture a quorum.

Passing a boolean where the count belongs is rejected rather than quietly read as 1. Under looser handling a stray true would silently downgrade a two-person control to a single approver, which is exactly the failure the field exists to prevent.