CampaignVault contract.
Statuses
Browse
Auth: none.rewardPerSlot, currency, perProviderLimit, closesAt, maxSubmitSeconds, proofRequirements[], slotCounts.OPEN, and providerBond before acting.
Brand: create and fund
The bounty only exists on-chain once the funding transaction is confirmed. Until then no slots can be claimed.
Provider: claim a slot
POST /api/v1/campaigns/:id/claim
Auth: session.intentId, expectedSlotIdHash, and a campaignClaimSlot intent. No slot exists yet — broadcast, then confirm:
id, and verify status: "CLAIMED" and boundTxHash. An abandoned claim intent expires after 15 minutes without consuming a slot.
Provider: submit proof
proofRequirements[].id. This persists the proof version and returns a campaignSubmitSlot intent; the slot stays CLAIMED or CHANGES_REQUESTED until you broadcast and confirm:
status: "SUBMITTED" and record reviewDeadline.
Brand: review
Each decision is prepare → broadcast → confirm. Every confirm body is{ "txHash": "0x…" }.
Both
request-changes and reject take { note, rejectedItems? }. Requesting changes is limited to two rounds.
Provider: challenge a rejection
POST /api/v1/campaigns/slots/:slotId/challenge
Auth: session. Returns adisputeId and a campaignOpenSlotChallenge intent with the evaluator panel committed in the calldata. There is no confirm endpoint — broadcast, then poll the slot and GET /api/v1/disputes/:disputeId until the slot is CHALLENGED and the dispute is in EVIDENCE_PHASE. See Disputes.
Timeout paths
A held slot stays
CLAIMED past its TTL — nothing auto-expires it. And there is no brand “close bounty” call: unfilled budget returns only through the permissionless CampaignVault.reclaimExpired after on-chain expiry, at which point the remaining OPEN slots are gone.Read your slots
Never infer completion from a successful broadcast — re-read until the expected status and transaction hash are projected.