Skip to main content

Overview

Proposals are merchant-created payment intents that buyers scan and settle. Each proposal contains:
  • Merchant information (DID, name, public key)
  • Line items and total amount
  • Deliverables (receipt, warranty)
  • Constraints (age gate, region restrictions)
  • Intent hash for on-chain settlement verification
All proposal endpoints require API key authentication.

Endpoints

Create Proposal

Create a new commerce proposal and generate a QR code.
POST /api/identipay/v1/proposals
Requires Authorization: Bearer {apiKey} header

Request Body

items
array
required
Array of line items in the purchase
amount
object
required
Total amount for the transaction
deliverables
object
required
What the merchant will deliver
constraints
object
Optional purchase constraints
expiresInSeconds
integer
default:900
Proposal expiry in seconds (max 86400 = 24 hours)

Response

transactionId
string
UUID for this transaction
intentHash
string
Hash of the proposal intent (used for on-chain verification)
qrDataUrl
string
Data URL of QR code image (base64-encoded PNG)
uri
string
Payment URI (identiPay protocol)
proposal
object
Full proposal object (JSON-LD CommerceProposal)
expiresAt
string
ISO 8601 timestamp when proposal expires

Example Request

curl -X POST https://api.identipay.com/api/identipay/v1/proposals \
  -H "Authorization: Bearer ip_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "name": "Coffee",
        "quantity": 2,
        "unitPrice": "5.00"
      }
    ],
    "amount": {
      "value": "10.00",
      "currency": "USDC"
    },
    "deliverables": {
      "receipt": true
    },
    "expiresInSeconds": 900
  }'

Example Response

{
  "transactionId": "550e8400-e29b-41d4-a716-446655440000",
  "intentHash": "a1b2c3d4e5f6...",
  "qrDataUrl": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
  "uri": "identipay://acme.com/550e8400-e29b-41d4-a716-446655440000",
  "expiresAt": "2026-03-09T21:15:00.000Z",
  "proposal": {
    "@context": "https://schema.identipay.net/v1",
    "@type": "CommerceProposal",
    "transactionId": "550e8400-e29b-41d4-a716-446655440000",
    "merchant": {
      "did": "did:identipay:acme.com:...",
      "name": "Acme Store",
      "suiAddress": "0xabc123...",
      "publicKey": "a1b2c3d4..."
    },
    "items": [
      {
        "name": "Coffee",
        "quantity": 2,
        "unitPrice": "5.00"
      }
    ],
    "amount": {
      "value": "10.00",
      "currency": "USDC"
    },
    "deliverables": {
      "receipt": true
    },
    "constraints": null,
    "expiresAt": "2026-03-09T21:15:00.000Z",
    "intentHash": "a1b2c3d4e5f6...",
    "settlementChain": "sui",
    "settlementModule": "0x...::settlement"
  }
}

Error Responses

Implementation Details

Proposal Creation Flow

When a proposal is created (see routes/proposals.ts:17-68):
  1. Authenticate: Verify API key and load merchant info
  2. Validate Input: Check all required fields and constraints
  3. Generate Transaction ID: Create UUID for this transaction
  4. Build Proposal: Create full JSON-LD CommerceProposal object
  5. Compute Intent Hash: Hash the proposal for on-chain verification
  6. Generate QR Code: Create QR code image and data URL
  7. Store Proposal: Save to database with status “pending”
  8. Return Response: Return transaction ID, QR code, and full proposal

Proposal Schema (JSON-LD)

Proposals follow the CommerceProposal schema:
{
  "@context": "https://schema.identipay.net/v1",
  "@type": "CommerceProposal",
  "transactionId": "uuid",
  "merchant": {
    "did": "did:identipay:...",
    "name": "...",
    "suiAddress": "0x...",
    "publicKey": "..."
  },
  "items": [...],
  "amount": {...},
  "deliverables": {...},
  "constraints": {...},
  "expiresAt": "ISO 8601",
  "intentHash": "...",
  "settlementChain": "sui",
  "settlementModule": "0x...::settlement"
}

Intent Hash

The intent hash is computed from:
  • Transaction ID
  • Merchant DID
  • Amount and currency
  • Expiry timestamp
  • Constraint requirements
This hash is verified on-chain during settlement to ensure the buyer is settling the exact proposal shown.

QR Code Format

QR codes encode a payment URI:
identipay://{merchant-hostname}/{transaction-id}
Wallets scan this QR code and resolve the full proposal via the /intents/:txId endpoint.

Proposal Lifecycle

Status Values:
  • pending: Awaiting payment
  • settled: Payment confirmed on-chain
  • expired: Expiry time passed
  • cancelled: Merchant cancelled

Automatic Expiry

A background task runs every 30 seconds to mark expired proposals:
setInterval(async () => {
  await db
    .update(proposals)
    .set({ status: "expired" })
    .where(
      and(
        eq(proposals.status, "pending"),
        lt(proposals.expiresAt, new Date())
      )
    );
}, 30_000);
  • Intents - Resolve proposal by transaction ID
  • Transactions - Check proposal status, submit settlement
  • WebSocket - Real-time proposal status updates

Build docs developers (and LLMs) love