Skip to main content
A Schema A BrandAttestation is the on-chain receipt that proves a brand slug on droplinked has been operator-reviewed and is who it says it is. This page walks the full lifecycle — request → operator approval → EAS Schema A mint → merchant polling → public verifier read — across the three audiences that touch it: merchants requesting from the shop builder, AI agents requesting on behalf of a merchant via MCP, and third-party verifiers reading the resulting on-chain attestation.
Schema A is currently anchored on Base Sepolia testnet. Mainnet flip is gated on the KMS-backed signer migration. Every endpoint below returns the active chain in its response so consumers can verify on the right easscan domain.

When to use this guide

  • Merchants — you clicked Request brand attestation in the shop builder and want to know what happens next.
  • AI agents — you’re orchestrating a merchant-onboarding flow via the droplinked MCP server and need to drive the same lifecycle programmatically.
  • Third-party verifiers (lenders, partners, regulators) — you need to resolve a brand attestation UID to chain-anchored facts without trusting droplinked.
If you only want the API contract for the two endpoints, jump straight to the Brand attestation request reference. If you want the architectural context for Schema A in the 4-axis trust fabric, see Trust Fabric overview.

The state machine

A brand-attestation request walks five discriminator values. Four are real queue-row states; the fifth (NONE) is a synthetic state the status endpoint returns when no row exists yet, so the widget can render the initial CTA without 404-handling.
No row in the queue for this shopSlug. The status endpoint returns { "status": "NONE", "request": null } and always 200. UIs render the Request brand attestation CTA in this state.
Merchant has clicked the CTA. A queue row is in SUPER_ADMIN review. Re-clicking the CTA is idempotent — the API returns the same requestId instead of duplicating the row. UIs render “Pending operator review — we’ll email you when it ships”.
Operator has approved the row. The mint orchestrator will attempt a Schema A on-chain mint. If the mint fails (RPC drop, gas spike, wallet-nonce contention) the row stays APPROVED; mintError + mintAttempts persist on the operator-side row for retry. The row is never auto-flipped back to PENDING or REJECTED.
Schema A attestation is on-chain. attestationUid + mintedAt are populated on the public projection. The widget can quote the easscan reference without a second round-trip. This is a terminal state — the row is never re-minted.
Operator declined the row with a reason (visible only on the SUPER_ADMIN console — never on the public projection). Terminal state. The merchant may submit a fresh PENDING row at any time; the partial-unique index on (shopSlug, status: PENDING) does NOT block re-submits after a terminal state.

Step 1 — Request the attestation

There are three equivalent paths to queue a request. They all hit the same backend endpoint and the same idempotency check.
1

Shop builder UI (merchant path)

From your merchant dashboard, open the Trust Fabric card and click Request brand attestation. Optionally add a free-text note (max 2048 chars — surfaces only to the operator). The card immediately flips to “Pending operator review”.No further action is required from the merchant. The status card polls GET /v2/attestations/brand/:shopSlug/request-status every 60s and auto-advances through PENDING → APPROVED → MINTED (or → REJECTED).
2

MCP `request_brand_attestation` tool (agent path)

An AI agent orchestrating onboarding calls the MCP tool wired against the droplinked MCP server.
const result = await mcp.callTool('request_brand_attestation', {
  shopSlug: 'unstoppable',
  notes: "Onboarding push initiated by agent on 2026-06-13",
});
{
  "requestId": "65f8a1b2c3d4e5f6a7b8c9aa",
  "status": "PENDING",
  "message": "Your request is in the operator review queue"
}
See Brand attestation MCP tools for the full envelope.
3

Direct POST (custom integration path)

If you’re driving the flow from your own backend or a third-party portal, hit the endpoint directly. No JWT, no IP-allowlist — the endpoint is @Public().
curl -X POST https://apiv3.droplinked.com/v2/attestations/brand/unstoppable/request \
  -H 'content-type: application/json' \
  -d '{ "notes": "Q3 partner pitch on 2026-07-15" }'
{
  "requestId": "65f8a1b2c3d4e5f6a7b8c9aa",
  "status": "PENDING",
  "message": "Your request is in the operator review queue"
}
Idempotent on (shopSlug, status: PENDING). Re-submitting while a PENDING row exists returns the same requestId — never a 4xx.
Fail-open by design. The endpoint hard-casts the response status to PENDING regardless of whether the row is new or re-found by the idempotency check. UIs should never have to disambiguate “first submit” vs “re-submit” — the discriminator alone drives the success toast.

Step 2 — Poll the status

Once a request is queued, walk the discriminator with the status endpoint. The widget polls this; an agent can long-poll it; a third-party portal can display it.
curl -s https://apiv3.droplinked.com/v2/attestations/brand/unstoppable/request-status | jq .
The endpoint always returns 200 — even when no row exists. Resolution rule: any PENDING row wins (there is at most one per slug by the partial index); otherwise the newest terminal row (createdAt desc) is returned.

Response shapes per state

{
  "status": "NONE",
  "request": null
}
No row in the queue. Render the initial CTA.
Operator-private fields stay private. decidedBy, decisionReason, mintError, mintAttempts, merchantId, and the merchant’s free-text notes are scrubbed from the public projection. They only surface on the SUPER_ADMIN admin route. The shape above is the full public surface.

Polling cadence

  • Merchant widget: 60s. The state walks PENDING → APPROVED → MINTED over operator-review timelines (minutes to hours); a 60s poll cadence feels live to the merchant without hammering the endpoint.
  • AI agent: prefer event-driven UX. If you can park the conversation and ping the merchant when state advances, do that. Otherwise long-poll at 30s intervals for up to 5 minutes — beyond that, hand the lifecycle back to the merchant.
  • Third-party verifier: don’t poll. Read once at decision time and cache the attestationUid — the EAS attestation is immutable once minted.

Step 3 — See the mint land on-chain

Once status === "MINTED", the attestationUid resolves to a Schema A attestation on the Ethereum Attestation Service. The easscan link scheme:
https://base-sepolia.easscan.org/attestation/view/<attestationUid>
For example (placeholder UID — your real UID will be different):
https://base-sepolia.easscan.org/attestation/view/0x9c4f7a3e8b1d2c6f5a0b8e9d1c2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f
The UID above is example-only — copy-pasting it will resolve to a “not found” page on easscan. Use the real attestationUid returned by your request-status response.
The easscan UI surfaces:
  • The Schema A definition (the BrandAttestation schema UID)
  • The issuer wallet — droplinked’s operator signer
  • The recipient — the merchant’s on-chain address
  • The decoded payload — brandSlug, displayName, issuedAt, expiresAt, and the registry pointers
  • A timeline of any subsequent revocations (Schema A revocations are rare and operator-driven; the reconciler never auto-revokes)
For the public verifier read without going through easscan, hit:
curl -s https://apiv3.droplinked.com/v2/attestations/brand/unstoppable | jq .
This returns the same chain-anchored fields plus the active-chain marker so your client picks the right easscan domain for hyperlinks.

Step 4 — For AI agents

Two MCP tools cover the full agent-side lifecycle, both published by the droplinked MCP server (mcp.droplinked.com):

request_brand_attestation

const result = await mcp.callTool('request_brand_attestation', {
  shopSlug: 'unstoppable',
  notes: 'Filed on behalf of merchant during agentic onboarding',
});
{
  "requestId": "65f8a1b2c3d4e5f6a7b8c9aa",
  "status": "PENDING",
  "message": "Your request is in the operator review queue"
}
Use this when an agent is orchestrating a merchant’s full onboarding flow — application form, lender match, brand-attestation request — and needs to queue the request without the merchant manually clicking the shop-builder CTA. The merchant must still own the shop slug; the tool does not bypass the operator review gate.

get_brand_attestation_status

const status = await mcp.callTool('get_brand_attestation_status', {
  shopSlug: 'unstoppable',
});
{
  "status": "MINTED",
  "request": {
    "requestId": "65f8a1b2c3d4e5f6a7b8c9aa",
    "shopSlug": "unstoppable",
    "status": "MINTED",
    "attestationUid": "0x9c4f7a3e8b1d2c6f5a0b8e9d1c2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f",
    "mintedAt": "2026-06-13T07:42:11Z",
    "createdAt": "2026-06-12T18:00:00Z"
  }
}
Use this in an agent loop to detect terminal state (MINTED or REJECTED) before handing back to a human. The five-state discriminator maps cleanly to a switch statement — same shape as the public HTTP endpoint, no envelope translation. A typical agent sequence:
  1. Call request_brand_attestation once at onboarding time.
  2. Call get_brand_attestation_status on a backoff (30s → 60s → 120s) until status is MINTED or REJECTED.
  3. On MINTED, call verify_brand_attestation (Schema A read) to confirm the on-chain envelope decodes cleanly. Then hand the merchant the easscan link from Step 3.
See Lender Trinity MCP Tools for the full catalog including these two tools.

Step 5 — For third-party verifiers

A third-party verifier (lender, partner, regulator) does not poll the request lifecycle. They consume the terminal artifact: the on-chain Schema A attestation referenced by attestationUid. The two canonical reads:

easscan UI (human verifier)

Open https://base-sepolia.easscan.org/attestation/view/<UID> in a browser. Decoded payload + issuer wallet + timeline are visible without running any infrastructure.

Droplinked public read (programmatic verifier)

curl -s https://apiv3.droplinked.com/v2/attestations/brand/<shopSlug> | jq .
Returns the chain-anchored claim envelope with the active-chain marker. Pair it with the public lender-registry endpoint to walk the trust trinity (brand → registered lender → underwritten credit-risk):
# 1. Brand attestation
curl -s https://apiv3.droplinked.com/v2/attestations/brand/unstoppable | jq .

# 2. Per-lender lookup if you need to verify a credit-risk attestation issuer
curl -s https://apiv3.droplinked.com/v2/lenders/crediblex-uae | jq .

# 3. Underwriting signals composite (Schema B + C rollup)
curl -s https://apiv3.droplinked.com/v2/underwriting-signals/<merchantId> | jq .
See Lender Registry Lookup + Underwriting Signals for sibling-axis reads.
Public verifier reads return only chain-anchored claims + aggregate identifiers — never the operator-side notes, decisionReason, decidedBy, or PII. Verifier-side policy decides whether the chain artifact is sufficient for the verifier’s risk threshold; droplinked never blocks reads on its own opinion of “good standing”.

Common errors

SymptomCauseFix
POST returns 404shopSlug not found via ShopServiceConfirm the slug matches the kebab-case URL slug, not the human-display name.
POST returns 400 “notes too long”notes exceeded 2048 charsTrim the note; only the operator sees it anyway.
GET request-status returns NONE after a successful POSTYou’re hitting a stale CDN cache (rare) or the wrong slugConfirm the slug. The endpoints are not edge-cached at the application layer.
Status stuck at APPROVED for >24hMint orchestrator hit a persistent RPC errorThe row is recoverable — operator-side mintError + mintAttempts drive the retry. Email support@droplinked.com with the requestId if it persists.
MINTED with a UID that doesn’t resolve on easscanWrong chain — you’re on the wrong easscan domainUse base-sepolia.easscan.org (testnet) until mainnet flip. The active chain is returned in the chain-anchored read at /v2/attestations/brand/:slug.
Repeated POST returns the same requestIdThis is expected — the endpoint is idempotent on (shopSlug, status: PENDING)Treat as success.

Trust Fabric (EAS Schema v2)

The 4-axis architecture context for Schema A and its sibling schemas.

Lender Trinity MCP Tools

Full catalog of MCP tools including request_brand_attestation and get_brand_attestation_status.

Brand attestation API reference

Endpoint-level reference for POST /request + GET /request-status.

Embed Trust-Fabric Widget

Drop-in widget for surfacing brand-attestation status on your own dashboard.