FIDREN Become a design partner
Developers

One decision call, before your agent pays.

Fidren answers whether a counterparty should be paid, up to how much, and under what conditions. You call it before the payment; what happens next is yours.

This page documents the API as it exists today. Where something is not built, it says so rather than describing it in the present tense.

Where Fidren fits

A decision layer, between evidence and execution. Not a replacement for either.

One layer of five.

Fidren does not ingest arbitrary third-party intelligence into the record today, and does not move your money. It turns a record into a recommended action, which is the step between the two that nothing else in a payment stack owns.

  1. Evidence settlement observed on chain and outcomes the parties report Upstream
  2. Fidren Decision API recommends an action from the record, the confidence in it, and the exposure you asked for Fidren
  3. A decision an action, a ceiling, confidence, reasons and evidence Fidren
  4. Your transaction policy decides what the answer means for this payment, alongside your own controls Yours
  5. Your wallet and payment rails enforce and execute. Fidren is not on this path Yours

Screening and blockchain-intelligence tools answer a different question and sit beside Fidren rather than inside it: there is no path today by which a third-party signal enters the record Fidren answers from. Run both, and combine them in your own transaction policy. Wallet and transaction-governance infrastructure sits downstream and does the enforcing. Fidren complements both; it is not a wallet, a custody provider, a payment processor or an execution engine.

Quickstart

No account. No API key. No card. No wallet. Copy the command, change the address, run it. There is no SDK to install — the integration is one HTTP call.

Your first decision, in one command.

POST /v1/decision
curl -s -X POST https://api.fidren.net/v1/decision \
  -H 'content-type: application/json' \
  -d '{
    "address": "0xf1d0000000000000000000000000000000000001",
    "chain": "base",
    "amount_cents": 5000,
    "currency": "USD"
  }'

That address is a documentation example with no history, so the answer is an honest REVIEW: Fidren says it does not know this counterparty rather than guessing. Change the address to one you care about and run it again.

Response
{
  "decision_id": "dec_a3b2fb82ab9e56577e3e8e23a37826e2",
  "decision": "REVIEW",
  "trust_score": 50,
  "exposure_level": 2,
  "confidence": "LOW",
  "recommended_limit_cents": 10000,
  "requested_amount_cents": 5000,
  "amount_status": "WITHIN_SUPPORTED_EXPOSURE",
  "payment_action": "REVIEW",
  "authorized_limit_cents": 0,
  "currency": "USD",
  "reasons": [
    "Insufficient observations to assess this counterparty; treat as unknown, not as adverse"
  ],
  "reason_codes": [
    { "code": "INSUFFICIENT_OBSERVATIONS" }
  ],
  "evidence": [
    { "code": "NO_HISTORY", "detail": "Insufficient observations to establish a track record", "value": 0 },
    { "code": "NEW_ADDRESS", "detail": "Address first observed 0 day(s) ago", "value": 0 }
  ],
  "evaluated_at": "2026-09-17T17:44:20.072Z",
  "disputed": false,
  "notification": "UNREACHABLE",
  "scoring_model_version": "score-v1.1.0",
  "decision_policy_version": "policy-v1.7.0"
}

Your decision_id and evaluated_at will differ. Everything else is the shape you receive.

What each field means

decision
What to do about this counterparty. One of five actions, not a score for you to interpret.
trust_score · confidence
How the record reads, and how much record there is. A low confidence score is not a bad counterparty — it is a thin one.
recommended_limit_cents
The exposure the evidence supports, independent of what you asked for.
amount_status · payment_action
These answer your amount, not the counterparty. The same party can be worth paying $50 and not worth paying $50,000, and these two fields are where that difference appears.
reasons · evidence
Why, in terms you can quote to whoever asks. Evidence carries codes and values, not prose alone.
evaluated_at
When the decision was made. Not the timestamp of the newest observation behind it.
scoring_model_version · decision_policy_version
Which model and which policy produced this answer, so an old decision stays re-derivable.
Amount-aware

The counterparty assessment and the payment decision are different answers to different questions.

Same counterparty. Same evidence. Different exposure.

amount_cents5000 — $50
recommended_limit_cents10000 — $100
amount_statusWITHIN_SUPPORTED_EXPOSURE
payment_actionREVIEW

amount_cents5000000 — $50,000
recommended_limit_cents10000 — $100, unchanged
amount_statusEXCEEDS_SUPPORTED_EXPOSURE
payment_actionDO_NOT_PAY_AS_REQUESTED

Same counterparty. Same evidence. Different exposure. Different payment decision. The trust score and the confidence are identical in both calls — only the amount moved.

Free tier

A commercial allowance, reported on every response.

100 free decisions per day, per IP.

The allowance is counted against the network origin Fidren observes for your request, and resets at 00:00 UTC. That origin comes from the connection itself, not from a header you can set — so it cannot be reset by changing one. Callers sharing one outbound IP, such as a team behind a single gateway, share one allowance. There is no signup: the first call is the first step, rather than a signup or a conversation with us.

Quota headers
Fidren-Tierfree
Fidren-Free-Limit100
Fidren-Free-Remaining99
Fidren-Free-Reset2026-09-18T00:00:00.000Z

What does not spend an allowance

  • A malformed request. A 400 costs you nothing.
  • A failure on our side. If Fidren cannot serve a decision, you are not charged an allowance for it.
  • A request the rate limiter refused — see below.

An allowance is only spent when a decision is actually produced and delivered.

Two different limits

A rate limit is not the end of your free tier.

They protect different things and answer with different codes.

Free quotaa commercial allowance — 100/day
Rate limitinfrastructure protection — requests per minute

429too fast. Slow down and retry; your allowance is untouched.

You can receive a 429 with your whole daily allowance still available. It means the burst was too fast, not that the free tier ran out.

Pay per decision

When the free allowance runs out.

The next valid decision returns 402 Payment Required with the terms. Pay $0.01 USDC on Base, retry with the signature, and the decision comes back. No account.

Fidren-Payment-Mode: x402 is a Fidren access preference, not part of the x402 standard: it asks to be billed immediately instead of spending the free allowance.

Authentication

One key, two accepted headers.

X-API-Key<your key>
AuthorizationBearer <your key>

Both are accepted because agent frameworks differ in which headers they can set. Keys are 32 random bytes, base64url-encoded, prefixed fdrn_. The plaintext is generated server-side and shown once at creation; the database stores only a SHA-256 digest, so a leaked database yields no usable credential. A missing, malformed or unrecognised key all return the same 401 with the same message, so error text cannot be used to enumerate valid keys.

A presented key is authoritative

If you send a key, that is the path you get. A valid one is served as Enterprise and spends no free allowance. An invalid one is 401 and stops there — it never falls back to the free tier and never falls back to being charged. A caller with a mistyped or expired key is exactly the one who must not be quietly billed, because they never asked to pay.

What the key model does not have

No rotation workflow — revoke and create. No expiry. No scopes and no per-key permissions. No IP restrictions. No separate test and live keys, and no sandbox: the key you are issued talks to the real service. No self-service creation. Plan your integration around a single long-lived credential you keep out of source control.

POST /v1/decision

Three required fields, two optional.

The request.

address required
The counterparty you would be paying. Any casing.
chain required
base or polygon. See the coverage note below — they are not equally served.
amount_cents required
The requested exposure, in USD-equivalent minor units. Integer, zero or more.
currency optional
USD, USDC or USDT. Defaults to USD. Echoed back and recorded; stablecoins are read 1:1 with USD.
service_identifier optional
A caller-side label for the service being paid. Yours, not interpreted.

The response.

All 17 fields below are present on every successful call.

decision_idThe durable name of this decision — dec_ followed by 32 hex characters. Store it beside your own payment record: it is how this answer is named in a later dispute. Opaque; do not parse it or infer anything from its ordering.
decisionOne of the five actions.
recommended_limit_centsThe per-transaction ceiling the record supports. Returned on every decision, including BLOCK.
requested_amount_centsThe amount_cents you sent, echoed back — the amount this decision was asked about.
currencyThe currency you sent, or USD if you sent none, echoed back. Recorded, not interpreted: it does not change the answer.
amount_statusWhere requested_amount_cents sits against the exposure the record supports. NO_SUPPORTED_EXPOSURE means the record supports none at all, so a smaller request would not help.
payment_actionWhat to do about this payment at this size — as distinct from decision, which speaks about the counterparty. DO_NOT_PAY_AS_REQUESTED withholds this payment at this size; it says nothing against the recipient, and a smaller request may be supported. Treat a value you do not recognise as unhandled and fail closed.
confidenceLOW, MEDIUM or HIGH — how much of a record the answer rests on. Never folded into the score.
trust_score0–100. Context for the action, never a substitute for it: act on decision, not on a threshold of your own over this number.
exposure_level0–4. The exposure tier this answer sits in, and where the ceiling comes from. Read it from the response — it is not a fixed function of trust_score.
reasonsPlain-language strings explaining the action.
evidenceNamed findings, each { code, detail, value? }. code is from a fixed set; value is an observation, never a weight.
evaluated_atWhen the answer was produced.
disputedThe assessed party is contesting this record, having proved control of the address. Confidence is held back; the decision is unaffected.
notificationNOTIFIED, PENDING, UNDELIVERABLE or UNREACHABLE — whether the assessed party could be told about an adverse record. UNREACHABLE is the common case.
scoring_model_versionThe model in force when this answer was produced.
decision_policy_versionThe policy in force when this answer was produced.

The per-dimension scoring breakdown is not in the response and is not published anywhere. Naming which lever moves a score would turn a risk signal into a checklist for gaming it.

Handling a decision

Fidren returns the action. You enforce it — there is no path by which Fidren does.

What to do with each answer.

ALLOW

Proceed with the payment you intended. recommended_limit_cents comes back and is not below the amount_cents you sent.

ALLOW_WITH_LIMIT

Treat recommended_limit_cents as the ceiling for this decision, whatever produced it. Cap the payment there, or change your request and call again — a smaller amount_cents will not necessarily change the action.

ALLOW_WITH_ESCROW

Route settlement through an escrow you choose and control, still within recommended_limit_cents. Fidren recommends the condition; it does not provide, operate or execute the escrow.

REVIEW

Do not treat REVIEW as a transient API error or put it in an automatic retry loop. Read reasons and evidence and route the transaction for review.

BLOCK

Decline the payment on these terms. Your infrastructure does the declining; Fidren blocks nothing. recommended_limit_cents still comes back and is not permission.

This is handling. What the five actions mean, and how confidence differs from risk, is on Product. Treat a Fidren answer as one input to your transaction policy rather than as the only control.

Chain coverage

The one place the contract is wider than the service.

Base is indexed. Polygon is not.

chain validates against base and polygon, but only Base is observed today — nothing indexes Polygon. A Polygon request is accepted and answered from an empty record, which will read as an unknown counterparty whatever that address has actually done. That is a materially different thing from support, so plan for Base.

Fidren is not chain-agnostic and this page will not describe it that way while one chain is served. When another lands, it will be named here.

Errors

One shape, whatever went wrong.

Every failure returns the same envelope.

The statuses below are the ones the contract documents. An unhandled fault returns the same shape with a 500, which the contract does not yet declare per route.

Error body
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "…",
    "details": [{ "field": "amount_cents", "message": "…" }]
  },
  "request_id": "…"
}

details is present only when there is something field-level to report.

400 VALIDATION_ERROR
The body failed validation. details carries a { field, message } per problem.
401 UNAUTHORIZED
A presented key that is missing its value, malformed, or not recognised — all three answer identically, so error text cannot be used to enumerate keys. An anonymous caller whose allowance is spent is not refused: they receive a 402 with the terms. Sending no credential at all while you still have free allowance returns 200, not 401.
402 PAYMENT_REQUIRED
Payment is required for this call: either your free allowance is spent, or you sent Fidren-Payment-Mode: x402 to skip it. The PAYMENT-REQUIRED header carries the terms — exact scheme, USDC on Base, $0.01, valid for 300 seconds. Pay and retry the same request with PAYMENT-SIGNATURE to receive the decision. An invalid key is never charged: it is 401 and stops there.
404 NOT_FOUND
No such resource. Not returned by the decision endpoint.
409 CONFLICT
The report cannot be appended as submitted: an invalid lifecycle transition, a side already reported by a different address, or a chain that moved during the write. Only the last is worth retrying.
429 RATE_LIMITED
Over the per-minute allowance.
503 SERVICE_UNAVAILABLE
A dependency is unreachable. Safe to retry.
Rate limits

A per-minute allowance, reported on every response.

X-RateLimit-Limitthe allowance for this caller
X-RateLimit-Remainingwhat is left in the current window
X-RateLimit-Resetwhen the window rolls
Retry-Afteron a 429 only — whole seconds to wait

The bucket is chosen from a credential Fidren issued, never from the one in the request: a key the service does not recognise buys nothing and falls back to an allowance keyed on your network address. Authenticated callers draw a larger per-minute allowance than anonymous ones. The budget belongs to your organisation, not to an individual key: issuing a second key splits the same allowance rather than adding another one. Read the headers rather than hard-coding a number — the allowance is a deployment setting, not a published plan and not a per-key negotiation.

Over the limit you get 429 with the same error envelope as every other failure, and a Retry-After in whole seconds. The window is a fixed minute, so that value is the time left in the current one: wait it out and retry.

Worth knowing before you scale

The counter is held in memory, per process, which is correct only because Fidren runs exactly one API instance — an invariant the service now enforces at start-up rather than merely documenting. Restarting it empties the counters, so a restart is worth at most one extra window to one caller. Restarting does not restore free decisions: the daily allowance is persisted in Postgres, so it survives restarts, deploys and any number of instances. Rate limiting is not billing — the per-minute limiter protects infrastructure, the daily allowance is a commercial quantity, and they are counted in different places for that reason.

Request IDs

The distinction that causes integration bugs.

A request ID is not a decision ID.

Every response carries x-request-id, and every error body repeats it as request_id. Supply your own on the request header and it is honoured when it matches a conservative pattern; anything else is replaced with a generated value rather than sanitised, because a header is caller-controlled and log files are read by tools that interpret what they find.

Two identifiers, two jobs

A request ID identifies one HTTP call, for support and correlation, and it is what a failed call gives you. A decision ID — decision_id, returned in the body of every successful decision — is the durable name of the decision itself. There is still no endpoint that reads a decision back over the API, so store the response when you receive it; keep the decision ID with it, and the record can be named unambiguously later.

Reporting outcomes

Not required to integrate. It is what makes the next answer better.

Telling Fidren how it went.

After an operation concludes, either side may report the outcome to POST /v1/receipts. Each side keeps its own hash chain and the server computes the links, so a reporter cannot reorder or edit its own account afterwards. Three levels are worth keeping apart:

Recorded
Name a side and an address in the request body. It is stored, attributed to your API key — no other endpoint or parameter is involved.
Signed
Optionally, the report carries a personal_sign signature from the reporting address. Fetch the statement from POST /v1/receipts/signing-message, sign it, resubmit. A signature is verified and stored only if it verifies. Only a signature shows the reporter controls the address it names.
Corroborated
Automatic once both sides have separately called this endpoint and their signed accounts agree — nothing further to call.

A reported outcome is never treated as verified truth — the chain establishes integrity and ordering against the reporter, not that the account is accurate. Separately signed reports can still come from parties under common control. Why that limit exists, and what it does and does not prove, is on Security.

Evaluating first

You can call it without acting on it.

A practical first step is to call Fidren alongside your existing flow, log the answers and compare them, without gating any payment on them. Nothing is switched on at Fidren's end to do that — there is no mode, no flag and no parameter, because Fidren never had the ability to act in the first place. What changes is only whether your code honours the answer.

Shadow Mode describes the posture. Security boundaries, the non-custodial architecture and what is redacted from logs are on Security.

Enterprise access

Optional. The free tier needs none of this.

Bring a system that is about to pay someone.

Free access requires no account and no API key. Enterprise credentials are provisioned per partner, for organisations that need higher volume or a commercial integration — not as a step on the way to trying Fidren.

The integration conversation is a short one when there is a real flow to point at. If you have one, we would like to hear about it.