the agent layer for complex backend engines

This is the last settings screen.

Annotate the classes your engine already has. Push from the terminal you already use. Your users configure by conversation — and a deterministic engine hands your code back its data, in its own types.

in production with our first host since July 2026
1,700+ conversations · every write gated · nothing authored in a dashboard

OBSOLETE

fig. 00 — a settings screen, 1984–2026 · 3 tabs · 41 fields · abandoned by most users

cohort — terminal@confiqure/cli
Scanned 12 files; 4 @Confiqure roots.
diff /campaigns rev 13 → 14
~ dailyBudget → dailyBudgetCap renamed — migration queued
+ espApiKey secret · masked turn
lint 0 errors · 1 note
composing ▮▮▮▮▮▮▮ 7/7
live in 41s 214 saved configurations migrated · none left behind
Campaign setupLIVE· /campaigns
instance Spring Drop · 2 of 4rev 14
>
configuration by conversation ·
this is a preview of the embed your users get

Four steps. None of them is a form.

Cohort is a campaign engine and confiqure's demo host. Everything below is what a developer there actually does — and what their user sees.

01 / annotate — on the classes the engine already has

The class is the contract.

No schema editor, no builder. @Confiqure goes on Cohort's real Campaign class. Comments become conduct. Types become contracts. Gates are yours to declare.

Campaign.javajava · multi-instance
@Confiqure(
    end      = "/campaigns",
    type     = Confiqure.Type.MULTI,
    callback = "https://api.cohort.io/hooks/confiqure"
)
public class Campaign {

    /** Ask for the campaign name first — it names the
     *  rest of the conversation. */
    private String name;

    /** Monthly budget, USD. */
    private BigDecimal monthlyBudget;

    /** Daily spend cap. Must fit the month — cap × 30 may not
     *  exceed monthlyBudget; if it would, refuse and offer the ceiling. */
    @Confiqure.Gate(requires = "monthlyBudget != null",
                    message  = "Set the monthly budget first — the daily cap depends on it.")
    private BigDecimal dailyBudgetCap;

    /** ESP API key. */
    @Confiqure.Secret(kind = SecretKind.TOKEN)
    private String espApiKey;

    @Confiqure.Consent(id = "google-ads",
        text = "Cohort reads campaign performance and syncs audiences. Nothing is written to your ad account.")
    private boolean googleAdsConnected;
}

02 / push — from the terminal, not a dashboard

One command. The diff does the paperwork.

Scan, diff, lint, compose. A renamed field becomes a migration — 214 saved configurations move over and nobody re-enters anything. A new secret gets a masked turn without a line of UI.

cohort — terminal@confiqure/cli
confiqure push
Scanned 12 files; 4 @Confiqure roots.
diff /campaigns rev 13 → 14
~ dailyBudget → dailyBudgetCap renamed — migration queued
+ espApiKey secret · masked turn
lint 0 errors · 1 note: budget comment names no currency — assuming USD
composing ▮▮▮▮▮▮▮ 7/7
live in 41s 214 saved configurations migrated · none left behind

03 / converse — the end user never sees a form

The AI converses. The engine enforces.

Maya leads with a daily cap before there is a budget. The gate you declared refuses the write — the model doesn't get to decide the order. Then the comment on the class does its job: the cap that doesn't fit the month is turned down and the ceiling is offered.

Campaign — Spring DropLIVE · /campaigns
cap daily spend at 800
dailyBudgetCap requires monthlyBudget != null

COHORT

Before I set a daily cap I need the month's budget — what is it for April?

12k

COHORT

$800 a day is $24,000 over April — twice the budget. $400 fits exactly. Take that, or front-load week one?

400 is fine
monthlyBudget $12,000dailyBudgetCap $800$400saved · PATCH /campaigns/spring-drop

04 / observe — the dashboard is an observatory

Everything lands on the instrument.

Conversations, saves, tokens, cost — read-optimized, authored nowhere. Endpoints appear here seconds after confiqure push. Status is text, not a lamp.

cohort / endpointslive 4 · composing 1 · failed 1
classendpointrevconvs 7dstatus
Campaign/campaigns1496LIVE
SendPolicy/send-policy512COMPOSING
AttributionSettings/attribution210FAILED

Why a model is allowed near your data.

Because it never gets the last word. Every write passes a deterministic gate before it is saved, and a record is delivered only when your own class accepts it. These are the gates, and what they say when they fire.

field validationtypes · bounds · formats
Emails, phones, dates, numbers, lengths, and patterns are checked against your class before a value is saved. A rejected value is asked for again — never guessed.
"That doesn't look like a phone number (expected digits like 555-123-4567)."
shape checkyour DTO is the schema
A value must fit the field's declared type. A string never lands in an object field; a single value never lands in a list. The engine reads your class, not a copy of it.
siteAnalysis expected object, got string
fabrication guardwhat the user typed is the truth
Digits the user never typed are never saved. The model cannot "complete" a phone number or pad a partial entry to make it pass.
saved digits are not what the user typed — refused
@Confiqure.Gateyour predicates, enforced by the engine
Rules you write on the class — comparisons, membership, sequencing — hold over the whole record. They are evaluated by the engine before a write, not requested of the model.
dailyBudgetCap requires monthlyBudget != null
@Confiqure.Gate(requires = "plan in {'pro','enterprise'} && seatCount >= 5")
claim checksaid it saved? prove it
If the model tells the user something was saved and no save happened, the turn is rejected and regenerated. The user never sees the false claim.
claim without save — turn suppressed, retried
green testdelivery gate
A configuration is delivered to your engine only when it deserializes into your class exactly — the same check your code would do.
delivered · Campaign.java · rev 14 · 41 s after push

Try it. This is a scripted preview of the widget.

Cohort's app, with confiqure embedded the way any host embeds it. Type into it — set a budget, push the cap past the gate, pause the campaign. The thread is scripted and reaches no engine; the real one runs in your sandbox after your first push.

MR

Campaigns

April 2026 · 4 campaigns · 2 running

AllRunningScheduledDraft
CampaignChannelsMonthly budgetLaunchesStatus
Spring DropEmail · Google Ads$12,000Apr 17Scheduled
Welcome SeriesEmail$1,800—Running
Cart RecoveryEmail · SMS$2,400—Running
Win-back MayEmail—May 2Draft
Campaign setupLIVE· /campaigns
instance Spring Drop · 2 of 4rev 14
>
configuration by conversation ·

Pay for conversations, not seats.

A conversation is one end user configuring one thing, start to finish. Everything else — endpoints, migrations, the widget, the CLI — comes with the workspace.

what you getBuildLaunchScale
$0forever$149/ monthtalk to us
conversations / month5005,000volume
endpoints3unlimitedunlimited
workspacessandbox + prodsandbox + prodas many as you run
saved-data migrations✓✓✓
bring your own model keys—✓✓
host-side tools & callbacks✓✓✓
supportcommunityemail · 1 business daydedicated · shared channel
Start buildingStart on LaunchBook a call

PLACEHOLDER · tier names, limits and prices are a proposal for this round — ratified numbers replace them before the page ships.

Ten minutes to the first push.

One annotation in your code, one CLI on your machine. The widget and the dashboard need nothing from you.

your terminalquick start
npm install -g @confiqure/cli
confiqure init -y
scanPaths: src/main/java · workspace linked
confiqure login
confiqure push
Scanned 12 files; 1 @Confiqure root.
live in 38s sandbox · /campaigns rev 1
confiqure push --production

Java · Spring Boot — ai.confiqure : confiqure-annotation-java : 3.0.0

what you add to your codeand nothing else
@Confiqure.Setting(end = "/settings")
public class StoreSettings {
    /** The store's display name. */
    private String name;
    /** Where refunds are sent. */
    private RefundPolicy refunds;
}

The questions engineers ask first.

What has to change in my code?
One annotation on the classes that hold your configuration, and a comment on each field saying how to ask for it. No schema files, no DTO copies, no settings UI. Your class stays the only definition of your configuration.
What happens when the model gets something wrong?
It can't reach your data. Every write goes through the gates above — validation, shape, fabrication, your own @Confiqure.Gate predicates — and a configuration is delivered only when it deserializes into your class. A wrong answer is refused and re-asked; it is never saved.
Where does the data go?
Back to your engine, in your own types, through the callback you declared. confiqure holds the conversation and the saved values; your engine remains the system of record for what they mean.
I renamed a field. Do my users re-enter everything?
No. A rename in your class becomes a migration at push time. Saved configurations are rewritten to the new key — and if one can't be migrated cleanly, it is held back from your engine and the user is asked to confirm next time, instead of your code reading a broken record.
How are secrets handled?
A field marked @Confiqure.Secret is collected in a masked turn of its own — never mixed into a regular question, never echoed back, encrypted at rest. The conversation history shows that a key was set, not the key.
Does the widget match my app?
It ships light, dark and auto — auto follows the visitor's OS. It sits in its own frame, so your styles never break it and its styles never leak into your page. Deeper brand theming is on the roadmap; tell us if you need it sooner.
Which languages?
Java and Spring Boot today, with a language-agnostic CLI. The annotation model is small on purpose — more runtimes follow the first hosts that need them.

git diff — your roadmap, after confiqure

Design and build the settings UI
Write storage and retrieval for user config
Version the schema; migrate old users by hand
Engineer prompts around your config
Re-test and re-deploy on every settings change
@Confiqure on the classes you already have

Annotate. Push. Done.