Skip to main content

Store Metadata

Store the mapping between policy ID and IPFS CID after creating an on-chain policy.

Endpoint

POST /api/storeMetadata
Content-Type: application/json
Authorization: Bearer {token}

Request Body

{
  "policyId": "abc123...",
  "cid": "QmXyz...",
  "sender": "0x742d...",
  "recipient": "0x1234...",
  "expiry": 1708608000,
  "maxAttempts": 3,
  "contentType": "file",
  "fileName": "document.pdf",
  "fileSize": 1048576
}

Parameters

FieldTypeRequiredDescription
policyIdstringUnique policy identifier (hash)
cidstringIPFS Content Identifier
senderaddressCreator’s wallet address
recipientaddressAuthorized recipient address
expirynumberUnix timestamp of expiration
maxAttemptsnumberMaximum access attempts
contentTypeenum"file" or "message"
fileNamestringOriginal filename (files only)
fileSizenumberSize in bytes (files only)

Response

Success (201)

{
  "success": true,
  "policyId": "abc123..."
}

Error (400)

{
  "error": "Invalid policyId format"
}

Error (409)

{
  "error": "Policy already exists"
}

Error (401)

{
  "error": "Unauthorized"
}

Example Usage

JavaScript

const response = await fetch('/api/storeMetadata', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${token}`,
  },
  body: JSON.stringify({
    policyId: '0xabc123...',
    cid: 'QmXyz789...',
    sender: '0x742d35...',
    recipient: '0x123456...',
    expiry: Date.now() / 1000 + 86400, // 24 hours
    maxAttempts: 3,
    contentType: 'file',
    fileName: 'report.pdf',
    fileSize: 2.5 * 1024 * 1024, // 2.5MB
  }),
});

const data = await response.json();

cURL

curl -X POST https://app.shieldhq.xyz/api/storeMetadata \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "policyId": "0xabc123...",
    "cid": "QmXyz...",
    "sender": "0x742d...",
    "recipient": "0x1234...",
    "expiry": 1708608000,
    "maxAttempts": 3,
    "contentType": "file"
  }'

Flow Integration

This endpoint is called after:
  1. Content encrypted client-side
  2. Encrypted content uploaded to IPFS
  3. Policy created on-chain
// Full flow
async function createSecureLink(file, recipient, expiry) {
  // 1. Encrypt
  const { encrypted, secretKey } = await encryptFile(file);

  // 2. Upload to IPFS
  const cid = await uploadToIPFS(encrypted);

  // 3. Create on-chain policy
  const policyId = await createPolicy(recipient, expiry, 3);

  // 4. Store metadata
  await fetch('/api/storeMetadata', {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${token}` },
    body: JSON.stringify({
      policyId,
      cid,
      sender: address,
      recipient,
      expiry,
      maxAttempts: 3,
      contentType: 'file',
      fileName: file.name,
      fileSize: file.size,
    }),
  });

  // 5. Return secure link
  return `${APP_URL}/access/${policyId}#${secretKey}`;
}

Validation

The API validates:
  • ✅ Authenticated session
  • ✅ Valid policyId format (0x + 64 hex chars)
  • ✅ Valid IPFS CID (Qm… or bafy…)
  • ✅ Valid Ethereum addresses
  • ✅ Future expiry timestamp
  • ✅ maxAttempts > 0

Rate Limits

LimitValue
Requests10 per minute
Per address5 per minute

Database Schema

Stored in PostgreSQL:
CREATE TABLE policies (
  policy_id VARCHAR(66) PRIMARY KEY,
  cid VARCHAR(64) NOT NULL,
  sender VARCHAR(42) NOT NULL,
  recipient VARCHAR(42) NOT NULL,
  expiry INTEGER NOT NULL,
  max_attempts INTEGER NOT NULL,
  attempts INTEGER DEFAULT 0,
  content_type VARCHAR(10) NOT NULL,
  file_name VARCHAR(255),
  file_size INTEGER,
  created_at TIMESTAMP DEFAULT NOW(),
  valid BOOLEAN DEFAULT TRUE
);