Skip to content

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');
Price amounts are in smallest units (6 decimals for 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:

  1. Validates input parameters
  2. Auto-detects MIME type if not provided
  3. Gets authentication headers (nonce + signature)
  4. Makes POST request to /v1/feeds/{feedId}/entries
  5. Stores content on IPFS
  6. 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: true for public access, or is_free: false with a price for paid content

Related