Sharp Trust, Merchant and PSP Integration Guide
Version: v3 · August 2026
Sharp Trust gives you a real-time risk decision on each agent-initiated transaction, before you authorize it. You send us the transaction, we return a recommendation (APPROVE, REVIEW, or DECLINE) with three separate scores and the reasons. That is the whole integration: one call in, one decision out.
You never send us a card number, CVV, or any payment credential. We only need transaction context. Staying out of your PCI scope is by design.
Getting started
- Open the partner portal (or click Get a test key from the docs).
- Sign in with Google (any verified work account).
- Set your organization name and type (Merchant or PSP).
- Click Create / rotate test key and copy the key immediately, it is shown once.
- Use Try the API on the same page (your key never goes in the browser), or call the API with curl or Postman (see below).
Test keys look like shai_test_... and are self-serve from the portal. Live keys (shai_live_...) are issued manually after production onboarding, email contact@sharp-labs.com.
The whole thing in one call
POST https://trust.sharp-labs.com/v1/assessments
Authorization: Bearer <your-api-key>
Content-Type: application/json
Send your API key on every request as Authorization: Bearer <key> (or an X-API-Key header). Keep it in your server's secrets, never in a browser or a public repo. You can rotate it from the portal at any time; each merchant's key is separate.
Minimum request (this is genuinely all that is required):
{
"platform_transaction_ref": "your-own-transaction-id",
"buyer_agent": { "external_ref": "the-agent-identifier" },
"amount": { "value": "48.00", "currency": "USD" },
"merchant_meta": { "name": "acme-subscriptions" }
}
Response:
{
"decision": "REVIEW",
"recommendation": "REVIEW",
"trust_score": 0.7,
"agent_trust": 0.7,
"transaction_risk": 0.3,
"confidence": 0.5,
"evidence_confidence": 0.5,
"reason_codes": [
"SIGNATURE_NOT_PROVIDED",
"SESSION_NOT_PROVIDED",
"MANDATE_NOT_PROVIDED",
"NO_VELOCITY_BASELINE",
"NEW_AGENT_LOW_HISTORY",
"CONTEXT_DEFAULT_PRIOR",
"SELLER_LOW_HISTORY"
],
"assessment_id": "0716d54b-c12f-4a36-a58c-5d42c6388fee",
"audit_ref": "audit_0716d54b-c12f-4a36-a58c-5d42c6388fee",
"created_at": "2026-08-24T21:10:00.000Z"
}
That is the expected cold-start shape for the minimum request. It is accepted, but returns REVIEW because no identity, mandate, or history was supplied. The score is low because coverage is low, not because the transaction is risky. That is why we return the three outputs below, so you can tell those two situations apart.
Three outputs, plus a recommendation
A single number cannot tell you whether a low score means genuine risk or simply not enough evidence. So every assessment returns three separate outputs:
| Output | What it means | Range |
|---|---|---|
agent_trust |
Trust in the agent and its authorization (identity, session, mandate, agent reputation). | 0 to 1, higher is safer |
transaction_risk |
Risk of this specific transaction (velocity, intent, merchant). | 0 to 1, higher is riskier |
evidence_confidence |
How much real evidence backed the assessment (coverage). Low here means "not enough data," not "risky." | 0 to 1 |
Then a single recommendation you act on:
recommendation |
decision |
What you do |
|---|---|---|
APPROVE |
PROCEED |
Continue to authorization |
REVIEW |
REVIEW |
Your choice: hold, step up, or continue with caution |
DECLINE |
BLOCK |
Stop the transaction |
DECLINE |
BLOCKED_BY_SCHEME |
The network declined it (only if you opt into scheme checks) |
recommendation and decision always agree; recommendation is the plain APPROVE / REVIEW / DECLINE label, decision is the internal value. Use whichever your systems prefer.
That is a complete integration. Everything below is optional and only makes the decision sharper.
Try it
export SHARP_TRUST_URL="https://trust.sharp-labs.com"
export SHARP_TRUST_KEY="<your-shai_test-key>"
curl -sS -X POST "$SHARP_TRUST_URL/v1/assessments" \
-H "Authorization: Bearer $SHARP_TRUST_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: demo-$(date +%s)" \
-d '{
"platform_transaction_ref": "your-own-transaction-id",
"buyer_agent": { "external_ref": "the-agent-identifier" },
"amount": { "value": "48.00", "currency": "USD" },
"merchant_meta": { "name": "acme-subscriptions" }
}'
Import the Postman collection into Postman, set apiKey, and send the same request. The full machine contract is at /docs/openapi.yaml.
Request fields
Required
| Field | Type | Notes |
|---|---|---|
platform_transaction_ref |
string | Your transaction id, retained for correlation and audit. |
buyer_agent |
object | The paying agent. { "external_ref": "..." } is enough. |
amount |
object | { "value": "48.00", "currency": "USD" }. Value is a decimal string. |
Recommended (cheap to send, improves accuracy)
| Field | Type | Notes |
|---|---|---|
merchant_meta |
object | The seller: { "name": "acme", "url": "acme.com", "lei": "..." }. url and lei unlock merchant-legitimacy checks (domain age, certificate transparency, GLEIF). |
seller_agent |
object | The seller agent, { "external_ref": "..." }, for counterparty reputation. |
intent_summary |
string | What the user asked for, e.g. "Subscribe to Acme Pro". |
Optional evidence (send whatever you already have)
All evidence goes under an evidence object. Missing fields lower confidence, they never cause an error.
| Field | What it adds |
|---|---|
evidence.mandates |
The user's authorization. { "amount_cap": "100.00", "currency": "USD", "counterparty": "acme", "effective_until": "2030-01-01T00:00:00Z" }. Optional amount_tolerance_percent / amount_tolerance_fixed for tax, shipping, tips, FX, and pre-authorization drift. See Assurance levels below. If you use Basis Theory, send { "allowance_id": "alw_..." } and we fetch it. For a natural-language authorization, send { "instruction": "spend up to $100 at Acme by Friday" } and we derive the mandate (marked as inferred assurance). AP2 verifiable mandates go in { "ap2": { ... } }. |
evidence.agent_signature |
A signed request proving the agent's identity: a Visa TAP signature, or an open Web Bot Auth signature verified against the provider's own key directory (add signature_agent). Add source_ip and we cross-check it against the provider's published infrastructure. |
evidence.intent |
{ "instruction": "...", "agent_context": "...", "item_description": "..." }. We scan agent_context (the untrusted text the agent read) for prompt-injection, and flag purchases that diverge from the user's intent. |
evidence.session |
Consumer / session assurance: { "human_verified": true, "session_bound": true, "delegation": { "scopes": [...], "expires_at": "..." }, "provider": "..." }. A valid agent can still act for a compromised account; this catches that. |
require_scheme_check |
boolean. Ask us to also run a network scheme check. |
Idempotency: send an Idempotency-Key header with your transaction id to make retries safe. Idempotency is isolated per merchant.
Assurance levels
An authorization interpreted from natural language must not carry the same weight as one that is cryptographically signed. A consistent mandate is capped by how strongly it is backed:
| Level | Source | Max weight | Reason code |
|---|---|---|---|
| Inferred | Derived from a free-text instruction | 0.60 | MANDATE_ASSURANCE_INFERRED |
| User-confirmed | The user confirmed the interpreted mandate (user_confirmed: true) |
0.78 | MANDATE_ASSURANCE_USER_CONFIRMED |
| Authenticated | Supplied over your authenticated channel | 0.95 | MANDATE_ASSURANCE_AUTHENTICATED |
| Cryptographic | Signed mandate or network-confirmed (e.g. a customer-approved Basis Theory allowance, or a verified AP2 proof) | 0.98 | MANDATE_ASSURANCE_CRYPTOGRAPHIC |
A mandate mismatch (amount, merchant, expiry) still blocks regardless of assurance. An amount over the cap but within tolerance passes with MANDATE_AMOUNT_WITHIN_TOLERANCE.
Response fields
| Field | Meaning |
|---|---|
recommendation |
APPROVE / REVIEW / DECLINE |
decision |
PROCEED / REVIEW / BLOCK / BLOCKED_BY_SCHEME |
trust_score |
0 to 1, overall, higher is safer |
agent_trust |
0 to 1, trust in the agent and its authorization |
transaction_risk |
0 to 1, risk of this transaction, higher is riskier |
evidence_confidence (and confidence) |
0 to 1, how much real evidence we had |
reason_codes |
Machine-readable reasons, see the catalog below |
assessment_id |
Our id for this decision |
audit_ref |
Reference for dispute evidence |
created_at |
Timestamp |
Store assessment_id and audit_ref against your transaction. They are your evidence trail.
Reason code catalog
Reason codes are stable strings. New ones may be added; treat unknown codes as informational.
Identity and session (agent_trust) AGENT_SIGNATURE_VALID, TAP_SIGNATURE_VALID, AGENT_INFRA_CORROBORATED, AGENT_INFRA_MISMATCH, AGENT_IP_PRIVATE, SIGNATURE_NOT_PROVIDED, TAP_SIGNATURE_INVALID, TAP_REPLAY_DETECTED, TAP_KEY_EXPIRED, SESSION_VERIFIED, SESSION_WEAK, SESSION_UNVERIFIED, SESSION_DELEGATION_EXPIRED, SESSION_ACCOUNT_COMPROMISED, SESSION_NOT_PROVIDED.
Mandate (agent_trust) MANDATE_CONSISTENT, MANDATE_ASSURANCE_INFERRED, MANDATE_ASSURANCE_USER_CONFIRMED, MANDATE_ASSURANCE_AUTHENTICATED, MANDATE_ASSURANCE_CRYPTOGRAPHIC, MANDATE_AMOUNT_MISMATCH, MANDATE_AMOUNT_WITHIN_TOLERANCE, MANDATE_CURRENCY_MISMATCH, MANDATE_COUNTERPARTY_MISMATCH, MANDATE_COUNTERPARTY_APPROX, MANDATE_EXPIRED, MANDATE_INVALID, MANDATE_NOT_FOUND, MANDATE_NOT_PROVIDED.
Intent and context (transaction_risk) INTENT_MATCH, INTENT_CLEAN, INTENT_PURCHASE_DIVERGENCE, PROMPT_INJECTION_DETECTED, PROMPT_INJECTION_SUSPECTED, INTENT_NOT_PROVIDED, CONTEXT_DEFAULT_PRIOR.
Velocity (transaction_risk) VELOCITY_NORMAL, VELOCITY_ELEVATED, VELOCITY_HIGH, AMOUNT_SPIKE, NO_VELOCITY_BASELINE.
Reputation (agent and seller) AGENT_TRUSTED_HISTORY, AGENT_CROSS_CLIENT_TRUST, AGENT_DISPUTE_HISTORY, AGENT_CHARGEBACK_HISTORY, AGENT_FRAUD_HISTORY, AGENT_FRAUD_UNVERIFIED, AGENT_REPUTATION_UNCORROBORATED, NEW_AGENT_LOW_HISTORY, and the SELLER_... equivalents.
Merchant legitimacy (transaction_risk) MERCHANT_ESTABLISHED, MERCHANT_LEGITIMACY_OK, MERCHANT_LEI_VERIFIED, MERCHANT_DOMAIN_NEW, MERCHANT_DOMAIN_VERY_NEW, MERCHANT_MALICIOUS_DOMAIN, MERCHANT_LEGITIMACY_UNKNOWN.
Add ?explainability=full support via the explainability field to receive a plain-English explanations map for the codes on a given decision.
Tell us the outcome (closes the loop)
When you know what happened to a transaction, report it. Outcomes drive cross-client reputation and give you validated ROI. This is a formal contract, not just chargebacks:
POST /v1/outcomes
Authorization: Bearer <your-api-key>
Content-Type: application/json
{
"transaction_ref": "your-own-transaction-id",
"type": "chargeback",
"final": true,
"source": "network",
"occurred_at": "2026-08-24T12:00:00Z",
"amount": 48.00
}
| Field | Notes |
|---|---|
transaction_ref |
The transaction this outcome belongs to (the platform_transaction_ref you sent). |
type |
authorization, capture, settlement, reversal, refund, dispute, chargeback, or fraud. |
final |
true for a final outcome, false for a preliminary one. Only final, reputationally-meaningful outcomes move reputation. |
source |
Who reported it: network, psp, merchant, or platform. More authoritative sources carry more weight. |
occurred_at |
ISO 8601 timestamp. |
amount, merchant_ref, mandate_ref, agent_ref, seller_ref |
Optional. Opaque ids only, never a PAN. |
Every outcome is written to a tamper-evident, hash-chained audit trail. Read and verify it:
GET /v1/audit-trail
Reporting outcomes is not required to start. It is the upgrade that turns "exposure blocked" into "loss avoided," and it is what makes reputation resistant to manipulation (a single non-authoritative claim is flagged, not acted on).
What you never send us
- No PAN (card number), no CVV, no cryptogram, no usable payment credential.
We are a decision layer, not a payment processor. We stay out of your PCI scope by design.
Advisory vs block mode
- Advisory (default): we return a recommendation and you stay in control.
Great for going live safely and comparing against your current flow.
- Block mode: you treat
DECLINEas a hard stop. Flip to this once you trust
the decisions.
Start in advisory. Move to block when you are comfortable.
To go live
- Integrate against
/v1/assessmentswith your test key and validate decisions. - Run in advisory alongside your current flow.
- Start sending outcomes to
/v1/outcomesso reputation and ROI accrue. - Contact contact@sharp-labs.com for a live key
(shai_live_...) and production onboarding.
- When you are ready, switch to block.
Questions or a field you are unsure about: reach out and we will map it with you. Integration timing depends on the merchant's environment, security review, and the signals enabled for that deployment.