Documentation Index
Fetch the complete documentation index at: https://docs.shieldhq.xyz/llms.txt
Use this file to discover all available pages before exploring further.
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
| Field | Type | Required | Description |
|---|
policyId | string | ✅ | Unique policy identifier (hash) |
cid | string | ✅ | IPFS Content Identifier |
sender | address | ✅ | Creator’s wallet address |
recipient | address | ✅ | Authorized recipient address |
expiry | number | ✅ | Unix timestamp of expiration |
maxAttempts | number | ✅ | Maximum access attempts |
contentType | enum | ✅ | "file" or "message" |
fileName | string | | Original filename (files only) |
fileSize | number | | Size 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:
- Content encrypted client-side
- Encrypted content uploaded to IPFS
- 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
| Limit | Value |
|---|
| Requests | 10 per minute |
| Per address | 5 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
);