Skip to main content
RapidDev - Software Development Agency
v0-integrationsNext.js API Route

How to Integrate Evernote with V0

To integrate Evernote with V0 by Vercel, generate a note browsing interface with V0, create a Next.js API route that calls the Evernote API to fetch notebooks, notes, and tags, store your Evernote developer token in Vercel environment variables, and deploy. Your app displays Evernote content through a secure server-side proxy without exposing your access token to the browser.

What you'll learn

  • How to generate an Evernote note browser interface with V0 and connect it to the Evernote API
  • How to install and use the evernote npm package in a Next.js API route
  • How to fetch Evernote notebooks, note metadata, and note content from a server-side route
  • How to store your Evernote developer token securely in Vercel environment variables
  • How to handle Evernote's Thrift-based API structure in TypeScript
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate15 min read35 minutesProductivityApril 2026RapidDev Engineering Team
TL;DR

To integrate Evernote with V0 by Vercel, generate a note browsing interface with V0, create a Next.js API route that calls the Evernote API to fetch notebooks, notes, and tags, store your Evernote developer token in Vercel environment variables, and deploy. Your app displays Evernote content through a secure server-side proxy without exposing your access token to the browser.

Build Custom Evernote Note Browsers with V0 and Next.js

Evernote remains a widely used personal knowledge management tool, particularly among professionals who have built large archives of notes over the years. While Evernote's native app provides note access, many developers want to display Evernote content within custom apps — showing relevant notes in a CRM, surfacing tagged notes in a project dashboard, or building a focused reading interface for specific notebooks. The Evernote API enables all of these use cases through programmatic access to notebooks, notes, tags, and search.

The Evernote API uses the Apache Thrift binary protocol, which is more complex than typical REST APIs. The official evernote npm package abstracts this complexity into a JavaScript client that makes it practical to work with. The key objects in Evernote's data model are NoteStore (the primary API interface for notes and notebooks), notebooks (containers for notes), notes (with metadata and content stored separately), and tags. Notes have a two-phase fetch pattern: first you fetch a list of note metadata (guid, title, created, updated, tags), then you fetch the full note content (ENML format — a subset of XHTML) separately for notes you want to display.

V0's strength in generating custom table and card interfaces is well-matched to Evernote's note listing and browsing use cases. The note search interface (search by tag, notebook, or keyword) makes an excellent V0 candidate since it's primarily a data display problem. V0 handles the layout; the Evernote API handling stays in the Next.js API routes. Note that Evernote's developer API access requires applying for API keys and agreeing to their developer terms — the process typically takes 1-3 days for approval.

Integration method

Next.js API Route

Evernote integrates with V0-generated Next.js apps through server-side API routes that proxy calls to the Evernote Thrift-over-HTTPS API. Your Evernote developer token is stored as a server-only Vercel environment variable. The React components V0 generates fetch note and notebook data from your Next.js routes, which in turn call Evernote's API using the evernote npm package. This keeps your access token secure and avoids CORS issues since Evernote's API does not support direct browser calls.

Prerequisites

  • An Evernote account — a free personal account works for development; a Premium or Professional account may be needed for API access to large archives
  • An Evernote developer token — generated at dev.evernote.com/doc/articles/dev_tokens.php after creating a developer account (sandbox tokens are available immediately; production tokens require API key approval)
  • The evernote npm package installed in your Next.js project — run npm install evernote in your project directory
  • A V0 account at v0.dev to generate the note browsing interface
  • A Vercel account to deploy the Next.js app and store environment variables

Step-by-step guide

1

Generate the Note Browsing Interface with V0

Open V0 at v0.dev and describe the note browsing or display interface you want to build. Evernote interfaces work well as two or three column layouts: a narrow notebook sidebar, a note list panel, and an optional note detail panel. For simpler interfaces (just a note list), a table or card grid is sufficient. Be explicit in your V0 prompt about the data fields you want to show — Evernote note metadata includes guid (the unique identifier used in API calls), title, created (Unix timestamp in milliseconds), updated (Unix timestamp in milliseconds), and tagNames (array of tag strings). Ask V0 to generate components that accept these fields so the wiring is clean. V0 generates React components with Tailwind CSS and shadcn/ui that handle the layout and interactivity. Since Evernote's API uses the evernote npm package (not a simple REST call), all API work happens in Next.js API routes. The V0 component just fetches data from your API routes via fetch calls. After generating the component, push to GitHub using V0's Git panel and then move on to creating the API routes.

V0 Prompt

Create a notebook and note browser with a responsive two-column layout. Left column: a scrollable list of notebook names with note counts (from /api/evernote/notebooks), with the active notebook highlighted. Right column: a list of notes for the selected notebook (from /api/evernote/notes?notebookGuid=ID) showing note title, last updated date formatted as 'Apr 15, 2026', and up to 3 tag badges per note. Clicking a note title opens a drawer on the right with the full note (from /api/evernote/note/GUID). Include a top search bar. Use a productivity app aesthetic with a white and light blue color scheme.

Paste this in V0 chat

Pro tip: Evernote timestamps are in Unix milliseconds (not seconds), so pass them directly to new Date(timestamp) in JavaScript — unlike some APIs that use seconds, Evernote timestamps are already milliseconds-precision.

Expected result: A note browser renders in V0's preview with a notebook sidebar, note list with metadata, and note detail panel with drawer. The component fetches from the Next.js API routes you'll create.

2

Install the Evernote Package and Create the API Routes

Install the evernote npm package in your Next.js project, then create the API routes for notebooks, note listing, and note content. Run npm install evernote in your project directory before writing the route code. The Evernote npm package provides an Evernote.Client that handles the Thrift protocol abstraction. You initialize the client with your developer token and the sandbox flag (false for production, true for the Evernote sandbox). The NoteStore is the primary API object — get it via client.getNoteStore(), then use noteStore.listNotebooks() for notebooks, noteStore.findNotesMetadata() for note lists with search filters, and noteStore.getNoteContent() for note content. The findNotesMetadata method accepts a NoteFilter (for search/notebook filtering) and a NotesMetadataResultSpec (for specifying which metadata fields to return). Always specify resultSpec.includeTitle = true and resultSpec.includeUpdated = true to get the fields you need for display. Note that the evernote package returns content in ENML format (a subset of XHTML) — you can display this in a React component using dangerouslySetInnerHTML after stripping the ENML wrapper tags, but be aware of XSS risks if the content is from untrusted sources.

app/api/evernote/notebooks/route.ts
1// app/api/evernote/notebooks/route.ts
2import { NextResponse } from 'next/server';
3import Evernote from 'evernote';
4
5export async function GET() {
6 const token = process.env.EVERNOTE_TOKEN;
7
8 if (!token) {
9 return NextResponse.json({ error: 'Evernote is not configured' }, { status: 500 });
10 }
11
12 const client = new Evernote.Client({
13 token,
14 sandbox: false, // true for dev.evernote.com sandbox
15 china: false,
16 });
17
18 try {
19 const noteStore = client.getNoteStore();
20 const notebooks = await noteStore.listNotebooks();
21
22 return NextResponse.json({
23 notebooks: notebooks.map((nb) => ({
24 guid: nb.guid,
25 name: nb.name,
26 updateSequenceNum: nb.updateSequenceNum,
27 defaultNotebook: nb.defaultNotebook || false,
28 })),
29 });
30 } catch (error) {
31 const message = error instanceof Error ? error.message : 'Unknown error';
32 console.error('Evernote notebooks error:', message);
33 return NextResponse.json(
34 { error: 'Failed to fetch notebooks', details: message },
35 { status: 500 }
36 );
37 }
38}
39
40// app/api/evernote/notes/route.ts
41// (separate file — shown here as a combined reference)
42// import { NextRequest, NextResponse } from 'next/server';
43// import Evernote from 'evernote';
44//
45// export async function GET(request: NextRequest) {
46// const { searchParams } = new URL(request.url);
47// const notebookGuid = searchParams.get('notebookGuid');
48// const tagName = searchParams.get('tag');
49// const words = searchParams.get('q');
50//
51// const filter = new Evernote.NoteStore.NoteFilter({
52// notebookGuid: notebookGuid || undefined,
53// tagGuids: undefined,
54// words: tagName ? `tag:${tagName}` : words || undefined,
55// });
56//
57// const resultSpec = new Evernote.NoteStore.NotesMetadataResultSpec({
58// includeTitle: true,
59// includeUpdated: true,
60// includeCreated: true,
61// includeTagGuids: true,
62// includeTagNames: true,
63// });
64//
65// const noteStore = client.getNoteStore();
66// const result = await noteStore.findNotesMetadata(filter, 0, 50, resultSpec);
67// return NextResponse.json({ notes: result.notes, totalNotes: result.totalNotes });
68// }

Pro tip: The Evernote sandbox environment (dev.evernote.com) uses a separate token from the production environment. Always test with the sandbox first and set sandbox: true in the Evernote.Client constructor during development.

Expected result: GET /api/evernote/notebooks returns a list of your Evernote notebooks with guid and name. Create a separate route at app/api/evernote/notes/route.ts using the same pattern to return note metadata for a given notebook.

3

Create the Note Content Route and Connect the UI

Create an API route for fetching individual note content and wire all routes to the V0-generated component. The note content endpoint needs a dynamic route to accept the note GUID: create app/api/evernote/note/[guid]/route.ts. The noteStore.getNote() method returns full note data including the ENML content. ENML is an XML-based format — to display it in a React component, strip the <?xml> declaration and <en-note> wrapper tags and pass the inner HTML using dangerouslySetInnerHTML. For production apps with sensitive data, sanitize the HTML with a library like DOMPurify before rendering. Update your V0 component to fetch notebooks on mount, then notes for the selected notebook, then note content when a note is clicked. The loading states V0 generates (skeleton cards while fetching) improve perceived performance since Evernote API calls can take 1-2 seconds for large note archives. Consider implementing client-side caching with React's useState or a simple in-memory Map to avoid re-fetching notebooks and note metadata on every click — Evernote data doesn't change frequently enough to need real-time freshness for most use cases.

V0 Prompt

Update the note browser so that clicking a notebook in the sidebar calls /api/evernote/notes?notebookGuid=GUID to load that notebook's notes. Clicking a note opens a side drawer and calls /api/evernote/note/GUID to fetch the note content. Display note content as HTML in the drawer (it will be sanitized ENML/HTML). Cache the notebook list and note metadata in component state so switching notebooks doesn't trigger redundant API calls. Show skeleton loaders during each fetch transition.

Paste this in V0 chat

app/api/evernote/note/[guid]/route.ts
1// app/api/evernote/note/[guid]/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import Evernote from 'evernote';
4
5export async function GET(
6 request: NextRequest,
7 { params }: { params: { guid: string } }
8) {
9 const token = process.env.EVERNOTE_TOKEN;
10
11 if (!token) {
12 return NextResponse.json({ error: 'Evernote is not configured' }, { status: 500 });
13 }
14
15 const { guid } = params;
16
17 if (!guid) {
18 return NextResponse.json({ error: 'Note GUID is required' }, { status: 400 });
19 }
20
21 const client = new Evernote.Client({
22 token,
23 sandbox: false,
24 china: false,
25 });
26
27 try {
28 const noteStore = client.getNoteStore();
29
30 // Fetch note with content
31 const note = await noteStore.getNote(
32 guid,
33 true, // withContent
34 false, // withResourcesData
35 false, // withResourcesRecognition
36 false // withResourcesAlternateData
37 );
38
39 // Strip ENML wrapper to get displayable HTML
40 let content = note.content || '';
41 // Remove XML declaration
42 content = content.replace(/<\?xml[^>]*>/, '').trim();
43 // Replace en-note wrapper with a div
44 content = content
45 .replace(/<en-note[^>]*>/, '<div class="enml-content">')
46 .replace(/<\/en-note>/, '</div>');
47
48 return NextResponse.json({
49 guid: note.guid,
50 title: note.title,
51 content,
52 created: note.created,
53 updated: note.updated,
54 tagNames: note.tagNames || [],
55 });
56 } catch (error) {
57 const message = error instanceof Error ? error.message : 'Unknown error';
58 console.error('Evernote note content error:', message);
59 return NextResponse.json(
60 { error: 'Failed to fetch note content', details: message },
61 { status: 500 }
62 );
63 }
64}

Pro tip: ENML content may include Evernote-specific elements like <en-todo> (checkboxes) and <en-media> (attachments) that won't render visually without CSS. Add basic CSS rules for these elements or strip them before displaying.

Expected result: Clicking a note in the browser opens a drawer with the note's formatted HTML content, title, tags, and last updated date. The content renders from Evernote's ENML format as readable HTML.

4

Add the Environment Variable and Deploy to Vercel

Push your code to GitHub and configure the Evernote developer token in Vercel. Open the Vercel Dashboard, navigate to your project, and go to Settings → Environment Variables. Add EVERNOTE_TOKEN with your Evernote developer token — for production use, this is the token from your approved Evernote API application. For initial development and testing, use the sandbox token from dev.evernote.com/doc/articles/dev_tokens.php, but remember to update to a production token and change sandbox: false before the final deployment. The developer token is a long alphanumeric string — do not add the NEXT_PUBLIC_ prefix, as this is a server-side secret. Set the variable for Production, Preview, and Development environments. Important: if you're in the Evernote sandbox (testing environment), set a separate EVERNOTE_SANDBOX variable so you can conditionally switch between sandbox and production based on the deployment environment. After adding the variable, trigger a redeployment. Test the live deployment by visiting /api/evernote/notebooks — you should see your notebooks list. Then test the full note browsing flow through the UI. For production Evernote API applications, the approval process requires submitting your use case to Evernote — plan for 1-3 business days for approval before going live.

Pro tip: Evernote sandbox and production environments use different tokens AND different API hostnames. The evernote npm package handles the hostname automatically based on the sandbox: true/false setting, but you must use the matching token for each environment.

Expected result: The Vercel deployment builds successfully, the notebook list loads from the Evernote API, and notes can be browsed and read from the deployed app. The EVERNOTE_TOKEN is working correctly in the server environment.

Common use cases

Personal Note Archive Browser

A custom read-only interface for browsing Evernote notebooks and notes with a cleaner design than the default Evernote app. Users can navigate notebooks, view note titles and metadata, and open notes in a reading panel. Useful for personal productivity dashboards or custom knowledge bases built on top of an existing Evernote archive.

V0 Prompt

Create a note archive browser with a left sidebar listing notebook names (from /api/evernote/notebooks) with note counts, a main panel with a table of notes showing title, created date, and tags as colored chips, and a right panel that shows note content when a note is selected (fetched from /api/evernote/notes/[guid]). Add a search bar above the notes table. Use a minimal, clean design with a sans-serif font and subtle borders.

Copy this prompt to try it in V0

CRM Notes Panel

A side panel in a CRM or project management tool that shows Evernote notes tagged with a specific client name or project identifier. Sales reps or project managers can view their meeting notes and research from within the CRM without switching to Evernote.

V0 Prompt

Build a notes panel component that takes a tagName prop and displays all Evernote notes with that tag (fetched from /api/evernote/notes?tag=CLIENT_NAME). Show each note as a card with its title, last updated date, and a preview of the first 200 characters. Include an 'Open in Evernote' link using the note's web URL. Style it as a compact side panel with a light gray background, suitable for embedding in a CRM sidebar.

Copy this prompt to try it in V0

Tagged Resource Library

A curated resource library built from Evernote notes tagged with specific topic tags. Visitors can filter by tag and search note titles. Each note appears as a resource card with the title, tags, and a snippet of content.

V0 Prompt

Design a resource library page that displays Evernote notes as resource cards (from /api/evernote/notes?notebook=NOTEBOOK_ID). Include a tag filter bar at the top, a search input, and a card grid where each card shows the note title, publication date, tag badges, and the first 150 characters of content. Add an external link button to open the note in Evernote web. Use a knowledge base style: white cards on a light gray background with clear typography.

Copy this prompt to try it in V0

Troubleshooting

API returns 'EDAMUserException: errorCode=AUTH_EXPIRED_TOKEN' or similar authentication error

Cause: The Evernote developer token is either invalid, expired, or a sandbox token being used against the production API (or vice versa). Developer tokens for the sandbox environment only work at sandbox.evernote.com, not at www.evernote.com.

Solution: Verify the token is correct at dev.evernote.com/doc/articles/dev_tokens.php. For production tokens, ensure you have approved API access from Evernote. Check that the sandbox parameter in Evernote.Client matches the token type — sandbox: true for dev.evernote.com tokens, sandbox: false for production tokens.

typescript
1// Sandbox token configuration:
2const client = new Evernote.Client({
3 token: process.env.EVERNOTE_TOKEN,
4 sandbox: process.env.NODE_ENV === 'development', // Use sandbox in dev
5 china: false,
6});

Note content displays as raw XML or ENML tags instead of readable text

Cause: The ENML content is being rendered without stripping the XML declaration and en-note wrapper, or dangerouslySetInnerHTML is not being used in the React component to render the HTML.

Solution: Strip the XML declaration and replace the en-note wrapper with a div before passing content to the component, then use dangerouslySetInnerHTML to render it: <div dangerouslySetInnerHTML={{ __html: sanitizedContent }} />. The code in Step 3 demonstrates the correct stripping approach.

typescript
1// In your React component:
2<div
3 className="note-content prose max-w-none"
4 dangerouslySetInnerHTML={{ __html: note.content }}
5/>

evernote package TypeScript errors: 'Module not found' or type errors

Cause: The evernote npm package has limited TypeScript support and may require type declaration adjustments. V0 generates TypeScript by default but the evernote package types may not be perfectly aligned with current TypeScript strict mode settings.

Solution: Add @types/evernote if available, or add a type declaration file. If imports fail, try importing as: import Evernote from 'evernote' or as a CommonJS require. Check that the package is installed: run npm list evernote in your project directory to verify installation.

typescript
1// If default import fails, try:
2const Evernote = require('evernote');
3// Or add to your tsconfig.json:
4// "esModuleInterop": true

EVERNOTE_TOKEN is undefined in the Vercel deployment

Cause: The environment variable was not configured in Vercel before deployment, was set after the latest deployment without triggering a redeploy, or was accidentally prefixed with NEXT_PUBLIC_.

Solution: Go to Vercel Dashboard → Settings → Environment Variables, verify EVERNOTE_TOKEN exists without any prefix, then redeploy from the Deployments tab. Environment variable changes require a new deployment to take effect.

Best practices

  • Always use sandbox: false in production and confirm it matches the type of token you're using — sandbox tokens are silently rejected by the production API
  • Cache Evernote notebook and note metadata responses in your component state to avoid unnecessary API calls when users navigate between notebooks
  • Sanitize ENML content with a library like DOMPurify before rendering with dangerouslySetInnerHTML, especially for notes that might contain external links or embedded content
  • Fetch only note metadata in the list view and fetch full note content on demand (lazy loading) to avoid slow initial page loads when notebooks have many notes
  • Use the resultSpec parameter in findNotesMetadata to request only the fields you display — avoiding unused fields reduces API response sizes for large notebooks
  • Remember that Evernote timestamps are in Unix milliseconds — use new Date(timestamp) directly (not new Date(timestamp * 1000)) unlike Unix second timestamps from some other APIs
  • For production apps, apply for official Evernote API access early in development — the approval process requires submitting your use case and can take several days

Alternatives

Frequently asked questions

Do I need to pay for an Evernote account to use the API?

A free Evernote account provides access to the developer API for testing through the sandbox environment. For production API access with real user data, you need to apply for API keys from Evernote. Evernote's API access approval may be limited to paid plan holders depending on their current developer policy — check dev.evernote.com for the current requirements as policies have changed several times over the years.

Can I create or edit Evernote notes from my V0 app?

Yes — the NoteStore provides createNote() and updateNote() methods. Creating a note requires building a Note object with title and ENML-formatted content. ENML content must be valid XML starting with the proper DOCTYPE declaration. For simple text notes, wrap the content in the ENML format: '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note>Your content here</en-note>'.

What is the difference between the Evernote sandbox and production environments?

The Evernote sandbox (dev.evernote.com) is a completely separate environment from production (evernote.com) with separate accounts, notes, and tokens. Your real Evernote notes are not accessible in the sandbox — you must create test data there separately. Sandbox tokens have the 'S=' prefix convention to distinguish them from production tokens. Always test in sandbox first, then switch to production tokens and sandbox: false before the final deployment.

How do I search Evernote notes from my app?

Use the Evernote Search Grammar in the NoteFilter.words parameter. Common search syntax: 'tag:projectname' for tag searches, 'notebook:"My Notebook"' for notebook-filtered searches, 'intitle:meeting' for title searches, and free-text terms for full-text search. Pass these as the words property in a new Evernote.NoteStore.NoteFilter object to findNotesMetadata.

Why does V0 not generate Evernote-connected code directly?

V0 is a UI generation tool focused on the visual layer of React applications. It generates components and basic fetch patterns, but specific API integrations like Evernote's Thrift-based API require custom server-side code using the evernote npm package. The pattern is: V0 generates the UI, you add the Next.js API routes connecting to Evernote.

What does ENML mean and how do I display it properly?

ENML stands for Evernote Markup Language — it's a restricted subset of XHTML used to store Evernote note content. It looks like HTML but includes Evernote-specific elements like <en-todo> (checkboxes) and <en-media> (attachments). To display ENML as readable HTML, strip the XML declaration and replace <en-note> with a <div>, then use dangerouslySetInnerHTML in React. For rich rendering including checkboxes and media, you need additional CSS and possibly custom element handling.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.