entries.create()
Add a new content entry to a feed.
Signature
entries.create(feedId: string, input: CreateEntryInput): Promise<Entry>Parameters
feedId
- Type:
string - Required: Yes
- Description: The UUID of the feed to add the entry to
input
- Type:
CreateEntryInput
interface CreateEntryInput {
// Either provide raw content (SDK will base64 encode it)
content?: string | Buffer | Blob | File | ArrayBuffer | object;
// OR provide pre-encoded base64 content (for advanced users)
content_base64?: string;
mime_type?: string; // Auto-detected if not provided
title?: string; // Entry title
description?: string; // Entry description
metadata?: Record<string, any>; // Additional metadata
tags?: string[]; // Entry tags
is_free?: boolean; // Free access (default: true)
expires_at?: number; // Unix timestamp expiration
price?: { // Price for paid entries
amount: string;
currency: string;
};
}Returns
- Type:
Promise<Entry>
interface Entry {
id: string;
feed_id: string;
cid: string;
mime_type: string;
title?: string | null;
description?: string | null;
metadata?: string | null;
tags: string[] | null;
is_free: boolean;
expires_at?: number | null;
is_active: boolean;
total_purchases: number;
total_revenue: string;
pinata_upload_id?: string | null;
piid?: string | null;
created_at: number;
updated_at: number;
}Usage
Basic Example
const entry = await grapevine.entries.create(feedId, {
content: 'This is my content',
title: 'My First Entry'
});
console.log('Entry created:', entry.id);Free Entry
// Create a free preview entry
const freeEntry = await grapevine.entries.create(feedId, {
title: 'Free Preview',
content: 'This content is available for free to everyone',
is_free: true,
tags: ['preview', 'free']
});With Description
// Entry with description
const detailedEntry = await grapevine.entries.create(feedId, {
title: 'Detailed Guide',
content: 'This is the full content of the guide...',
description: 'A comprehensive guide to blockchain technology',
tags: ['blockchain', 'guide']
});JSON Content
// Create JSON data entry - can pass object directly
const jsonData = {
type: 'market-data',
symbol: 'BTC',
price: 50000,
volume: 1000000
};
const dataEntry = await grapevine.entries.create(feedId, {
title: 'Market Update',
content: jsonData, // Pass object directly
mime_type: 'application/json',
tags: ['data', 'market']
});Markdown Content
// Create markdown entry
const markdownContent = `
# Technical Analysis
## Overview
Today's market shows...
- Point 1
- Point 2
- Point 3
`;
const mdEntry = await grapevine.entries.create(feedId, {
title: 'Daily Analysis',
content: markdownContent,
mime_type: 'text/markdown',
tags: ['analysis', 'daily']
});Paid Entry (Requires Payment to View)
Create premium content that requires x402 micropayment to access:
// Create a paid entry - viewers must pay to access
const paidEntry = await grapevine.entries.create(feedId, {
title: 'Premium Market Report',
content: `
# Q4 2024 Market Analysis
## Executive Summary
This comprehensive report covers...
[Full premium content here]
`,
description: 'In-depth quarterly analysis with actionable insights',
mime_type: 'text/markdown',
tags: ['premium', 'analysis', 'quarterly'],
is_free: false,
price: {
amount: '1000000', // 1.00 USDC (6 decimals)
currency: 'USDC'
}
});
console.log('Paid entry created:', paidEntry.id);
console.log('Price:', paidEntry.price, 'USDC');'1000000'= 1.00 USDC'500000'= 0.50 USDC'100000'= 0.10 USDC'10000'= 0.01 USDC
Paid Entry with Expiration
Create time-limited premium content:
// Paid entry that expires in 7 days
const expiringEntry = await grapevine.entries.create(feedId, {
title: 'Weekly Alpha Report',
content: 'This week\'s exclusive trading signals...',
description: 'Time-sensitive trading opportunities',
tags: ['alpha', 'trading', 'weekly'],
is_free: false,
price: {
amount: '2500000', // 2.50 USDC
currency: 'USDC'
},
expires_at: Math.floor(Date.now() / 1000) + (7 * 24 * 60 * 60) // 7 days
});File Uploads
The SDK supports multiple file input types and automatically handles base64 encoding.
Upload File from Disk (Node.js/Bun)
import { readFileSync } from 'fs';
import path from 'path';
// Upload a PDF document
const pdfBuffer = readFileSync('./reports/quarterly-report.pdf');
const pdfEntry = await grapevine.entries.create(feedId, {
title: 'Q4 2024 Quarterly Report',
content: pdfBuffer,
mime_type: 'application/pdf',
description: 'Full quarterly financial report',
tags: ['report', 'quarterly', 'pdf'],
is_free: false,
price: { amount: '5000000', currency: 'USDC' } // 5.00 USDC
});
console.log('PDF uploaded:', pdfEntry.cid);Upload Image (Node.js/Bun)
import { readFileSync } from 'fs';
// Upload an image
const imageBuffer = readFileSync('./images/chart.png');
const imageEntry = await grapevine.entries.create(feedId, {
title: 'Market Chart - December 2024',
content: imageBuffer,
mime_type: 'image/png',
description: 'Technical analysis chart',
tags: ['chart', 'analysis', 'image']
});
console.log('Image uploaded:', imageEntry.cid);Upload from Browser File Input
// React example with file input
function FileUploader({ feedId, grapevine }) {
const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;
try {
const entry = await grapevine.entries.create(feedId, {
title: file.name,
content: file, // Pass File object directly
mime_type: file.type,
description: `Uploaded file: ${file.name}`,
tags: ['upload']
});
console.log('File uploaded:', entry.cid);
} catch (error) {
console.error('Upload failed:', error);
}
};
return (
<input
type="file"
onChange={handleFileUpload}
accept=".pdf,.png,.jpg,.jpeg,.mp4,.mp3"
/>
);
}Upload with Blob (Browser)
// Create entry from a Blob (e.g., from canvas or fetch)
const response = await fetch('https://example.com/data.json');
const blob = await response.blob();
const blobEntry = await grapevine.entries.create(feedId, {
title: 'External Data',
content: blob,
mime_type: 'application/json',
tags: ['external', 'data']
});Upload Video
import { readFileSync } from 'fs';
// Upload a video file
const videoBuffer = readFileSync('./videos/tutorial.mp4');
const videoEntry = await grapevine.entries.create(feedId, {
title: 'Trading Tutorial',
content: videoBuffer,
mime_type: 'video/mp4',
description: 'Step-by-step trading guide',
tags: ['video', 'tutorial'],
is_free: false,
price: { amount: '10000000', currency: 'USDC' } // 10.00 USDC
});Pre-encoded Base64 (Advanced)
For cases where you already have base64-encoded content:
// If you already have base64 data (e.g., from another API)
const base64Data = 'SGVsbG8sIFdvcmxkIQ=='; // "Hello, World!"
const entry = await grapevine.entries.create(feedId, {
title: 'Pre-encoded Content',
content_base64: base64Data, // Use content_base64 instead of content
mime_type: 'text/plain'
});Batch Upload Files
import { readFileSync, readdirSync } from 'fs';
import path from 'path';
// Upload all markdown files from a directory
const articlesDir = './articles';
const files = readdirSync(articlesDir).filter(f => f.endsWith('.md'));
const entries = files.map(filename => ({
title: filename.replace('.md', ''),
content: readFileSync(path.join(articlesDir, filename)),
mime_type: 'text/markdown',
tags: ['article', 'blog']
}));
const results = await grapevine.entries.batchCreate(feedId, entries, {
onProgress: (completed, total) => {
console.log(`Uploaded ${completed}/${total} files`);
},
delayMs: 500
});
console.log(`Successfully uploaded ${results.successful.length} files`);Behind the Scenes
This method:
- Validates input parameters
- Auto-detects MIME type if not provided
- Gets authentication headers (nonce + signature)
- Makes POST request to
/v1/feeds/{feedId}/entries - Stores content on IPFS
- Returns created entry with
cid(IPFS content identifier)
Error Handling
try {
const entry = await grapevine.entries.create(feedId, {
content: 'New content',
title: 'New Entry'
});
} catch (error) {
if (error.message.includes('404')) {
console.error('Feed not found');
} else if (error.message.includes('403')) {
console.error('Not authorized - you can only add to your own feeds');
} else if (error.message.includes('401')) {
console.error('Authentication failed');
} else if (error.message.includes('400')) {
console.error('Invalid input');
} else {
console.error('Error:', error.message);
}
}Notes
- Authentication: Required - must be feed owner
- Free to Create: Creating entries does not require payment
- MIME Type: Auto-detected from content if not specified
- Private IPFS Storage: Content stored on Private IPFS, returns
cid - Ownership: Can only add entries to feeds you own
- Free vs Paid Content: Mark
is_free: truefor public access, oris_free: falsewith apricefor paid content
Related
- entries.batchCreate() - Create multiple entries
- entries.list() - List feed entries
- entries.delete() - Delete an entry