Connect Bolt.new to Evernote using Evernote's REST API with OAuth 1.0a for user authentication or a developer token for personal use. Fetch notebooks and notes, search notes with Evernote's query grammar, and display content in React. Note: Evernote uses OAuth 1.0a (older protocol) and ENML (XML-based content format) — use the oauth-1.0a npm package for auth and convert ENML to HTML for rendering. OAuth callback flows require deployment to a public URL.
Access Evernote Note Data in Your Bolt.new App
Evernote remains one of the largest note-taking repositories in the world, with millions of users storing years of personal and professional knowledge in its platform. For developers building personal productivity tools, custom note interfaces, or knowledge management applications on top of existing Evernote data, the Evernote API provides access to notebooks, notes, tags, and note content.
Evernote's API has two authentication paths. For personal use — building a tool just for yourself — a developer token provides immediate API access without any OAuth flow. Go to dev.evernote.com, log in with your Evernote account, and a developer token for your account is displayed. This token grants full API access to your own notes. For apps where other users will authorize access to their own Evernote accounts, OAuth 1.0a is required. Unlike the more common OAuth 2.0 (used by Notion, Stripe, and most modern APIs), OAuth 1.0a involves signed requests and a more complex multi-step handshake.
The most important technical consideration for Evernote integration is the note content format. Evernote Note Markup Language (ENML) is a subset of XHTML with custom namespaces for Evernote-specific elements. Note bodies are stored as XML strings that must be parsed and converted to standard HTML for browser rendering. The evernote npm package provides utilities for this conversion. Attachments (images, PDFs, documents) are referenced as resources in the ENML and must be fetched separately via the resource API. For most Bolt integration use cases, displaying note titles, notebook names, tags, and plain text excerpts is sufficient without full ENML rendering.
Integration method
Bolt generates Next.js API routes that call Evernote's API using either a developer token (personal use) or OAuth 1.0a (multi-user apps). Evernote's API is thrift-based with an HTTP gateway, and note content uses ENML format that must be converted to HTML for display. Developer token access works in Bolt's WebContainer preview. OAuth 1.0a callback flows require a deployed public URL since the WebContainer cannot receive OAuth redirects from Evernote's authorization servers.
Prerequisites
- A Bolt.new account with a Next.js project
- An Evernote account at evernote.com (free plan works for personal developer tokens)
- An Evernote developer token from dev.evernote.com (for personal use) or an API key from the Evernote Developer portal (for multi-user apps)
- Notes or notebooks in your Evernote account to test with
Step-by-step guide
Get an Evernote Developer Token and Note Store URL
Get an Evernote Developer Token and Note Store URL
For personal applications accessing your own Evernote data, a developer token is the simplest and fastest authentication method — no OAuth dance, no callback URL setup, just a static token that grants full API access to your account. To get a developer token, navigate to dev.evernote.com in your browser. Click the link to get a developer token — you will need to log in with your Evernote account credentials if not already logged in. On the developer token page, you will see two important values: your developer token (a long alphanumeric string) and your Note Store URL (a URL like https://www.evernote.com/shard/s1/notestore). Copy both values immediately — the developer token is your API credential, and the Note Store URL is the endpoint URL for all API calls specific to your account shard. Evernote accounts are distributed across multiple shards (s1, s2, etc.) based on account creation date and region. The Note Store URL is shard-specific — using the wrong shard URL will result in API errors even with a valid token. Always use the exact Note Store URL shown on the developer token page for your account, not a generic Evernote API URL. Add both values to your .env file: EVERNOTE_DEVELOPER_TOKEN and EVERNOTE_NOTE_STORE_URL. These must not have the NEXT_PUBLIC_ prefix — Evernote tokens grant full account access and must never be exposed in client-side JavaScript. For apps where other Evernote users will log in to access their own notes, OAuth 1.0a is required. This involves requesting a temporary token, redirecting the user to Evernote's authorization page, receiving a callback with a verifier, and exchanging it for an access token. The callback URL must be a publicly accessible URL — the Bolt WebContainer preview cannot receive OAuth callbacks. Register your deployed URL (Netlify or Bolt Cloud) as the OAuth callback URI in the Evernote developer portal. For multi-user apps, recommend Notion as a simpler modern alternative with OAuth 2.0.
Add EVERNOTE_DEVELOPER_TOKEN and EVERNOTE_NOTE_STORE_URL to the .env file with placeholder values. Create a lib/evernote.ts utility that exports an evernoteGet helper function. It should make requests to the Evernote Thrift HTTP API using the developer token. For simplicity, use the Evernote REST-like endpoints at {NOTE_STORE_URL} with the token as a query parameter (token={EVERNOTE_DEVELOPER_TOKEN}). Export fetchNotebooks and fetchNotesMeta helper functions.
Paste this in Bolt.new chat
1// lib/evernote.ts2// Evernote API uses Thrift protocol over HTTP.3// For note listing and metadata, we use the JSON/REST-compatible endpoints.4// Full ENML content requires the evernote npm package or direct Thrift calls.56const EVERNOTE_TOKEN = process.env.EVERNOTE_DEVELOPER_TOKEN;7const NOTE_STORE_URL = process.env.EVERNOTE_NOTE_STORE_URL;89function getBaseUrl(): string {10 if (!NOTE_STORE_URL || !EVERNOTE_TOKEN) {11 throw new Error('EVERNOTE_DEVELOPER_TOKEN and EVERNOTE_NOTE_STORE_URL must be set in .env');12 }13 return NOTE_STORE_URL;14}1516export interface EvernoteNotebook {17 guid: string;18 name: string;19 defaultNotebook: boolean;20 serviceCreated: number;21 serviceUpdated: number;22}2324export interface EvernoteNoteMetadata {25 guid: string;26 title: string;27 created: number;28 updated: number;29 notebookGuid: string;30 tagGuids?: string[];31 contentLength?: number;32}3334// Fetch using the evernote npm package (install: npm install evernote)35import Evernote from 'evernote';3637export function getEvernoteClient() {38 return new Evernote.Client({39 token: EVERNOTE_TOKEN,40 sandbox: false,41 china: false,42 });43}4445export async function fetchNotebooks(): Promise<EvernoteNotebook[]> {46 const client = getEvernoteClient();47 const noteStore = client.getNoteStore();48 // @ts-expect-error - Evernote SDK types are loose49 const notebooks = await noteStore.listNotebooks();50 return notebooks as EvernoteNotebook[];51}5253export async function fetchNotesMeta(54 options: { notebookGuid?: string; words?: string; maxNotes?: number } = {}55): Promise<EvernoteNoteMetadata[]> {56 const client = getEvernoteClient();57 const noteStore = client.getNoteStore();5859 const filter: Record<string, unknown> = {};60 if (options.notebookGuid) filter.notebookGuid = options.notebookGuid;61 if (options.words) filter.words = options.words;6263 const spec = { includeTitle: true, includeCreated: true, includeUpdated: true, includeNotebookGuid: true, includeTagGuids: true };6465 // @ts-expect-error - Evernote SDK types are loose66 const result = await noteStore.findNotesMetadata(filter, 0, options.maxNotes || 20, spec);67 return (result.notes || []) as EvernoteNoteMetadata[];68}Pro tip: The Evernote npm package (npm install evernote) provides the official Thrift client for Node.js, which is the most reliable way to interact with Evernote's API. The REST-style HTTP endpoints are undocumented and less stable than the Thrift protocol. Install the package and use getEvernoteClient() for all API interactions.
Expected result: The lib/evernote.ts helper is configured. Install the evernote package (npm install evernote) in Bolt's terminal and test fetchNotebooks() from an API route — you should see your Evernote notebooks returned as an array with names and GUIDs.
Build API Routes for Notebooks and Note Metadata
Build API Routes for Notebooks and Note Metadata
With the Evernote client configured, build the Next.js API routes that your React components will call. Notebook and note metadata listing are the safest starting points because they involve minimal data volume and no ENML parsing complexity. The listNotebooks call returns all notebooks in the account, including the default notebook, shared notebooks, and linked notebooks. Each notebook has a guid (unique identifier), name, defaultNotebook boolean, and timestamp fields for creation and update time. The GUID is what you use to filter notes to a specific notebook — store it in component state when the user selects a notebook. Note metadata (from findNotesMetadata) is distinct from note content. Metadata includes the title, creation date, update date, notebook GUID, and tag GUIDs without fetching the full note content. This is efficient for building list views — you get everything needed to display a note row without the overhead of fetching each note's ENML body. Full content is fetched separately on demand when the user clicks a specific note. Evernote's search grammar (Evernote Search Grammar, documented at dev.evernote.com) supports keyword search, notebook-scoped search (notebook:MyNotebook), tag filtering (tag:important), date ranges (created:month-1), and full-text content search. Pass search strings directly as the words parameter in the NoteFilter. The search indexes are maintained server-side, so full-text search across thousands of notes is fast. For the note list API, handle the case where a user has many notes by limiting results with maxNotes and implementing a page offset parameter. The findNotesMetadata call accepts offset and maxNotes parameters for pagination. Include the total note count from the result for displaying 'Showing X of Y notes' in the UI.
Create two Next.js API routes. First, /api/evernote/notebooks/route.ts that calls fetchNotebooks from lib/evernote.ts and returns the notebook list sorted alphabetically by name. Cache for 5 minutes. Second, /api/evernote/notes/route.ts that accepts query params: notebookGuid (optional), search (optional), offset (default 0), limit (default 20). Call fetchNotesMeta with those params. Return { notes: NoteMetadata[], offset, total }. Add a 30-second cache keyed by the full param combination. Handle errors with descriptive messages.
Paste this in Bolt.new chat
1// app/api/evernote/notebooks/route.ts2import { NextResponse } from 'next/server';3import { fetchNotebooks } from '@/lib/evernote';45let notebooksCache: { data: unknown; expiresAt: number } | null = null;67export async function GET() {8 if (notebooksCache && Date.now() < notebooksCache.expiresAt) {9 return NextResponse.json(notebooksCache.data);10 }1112 try {13 const notebooks = await fetchNotebooks();14 const sorted = notebooks.sort((a, b) => a.name.localeCompare(b.name));15 const result = { notebooks: sorted, total: sorted.length };16 notebooksCache = { data: result, expiresAt: Date.now() + 5 * 60 * 1000 };17 return NextResponse.json(result);18 } catch (err) {19 const message = err instanceof Error ? err.message : 'Failed to fetch notebooks';20 return NextResponse.json({ error: message }, { status: 500 });21 }22}Pro tip: Evernote's API can be slow for accounts with large note libraries — the initial listNotebooks call can take 2-5 seconds. Add a generous cache TTL (5 minutes for notebooks, 30 seconds for note lists) to prevent repeated slow loads. Show a loading spinner for first loads and use the cached data for subsequent page views.
Expected result: The notebooks API route returns your Evernote notebooks as a sorted array. The notes route returns note metadata including titles, creation dates, and notebook GUIDs — enough information to build a full list view without fetching any note content.
Fetch and Convert ENML Note Content to HTML
Fetch and Convert ENML Note Content to HTML
Displaying the full body of an Evernote note requires fetching the note content and converting ENML to renderable HTML. ENML is a well-formed XML format with a specific root element structure and Evernote-specific custom elements. A full ENML note content string looks like this: the document starts with an XML declaration and DOCTYPE referencing Evernote's ENML DTD, followed by an en-note root element containing the note body. The body uses standard HTML-like elements (p, br, div, span, a, table, tr, td, ul, li) plus two Evernote-specific elements: en-media (for embedded attachments and images, identified by resource hash) and en-todo (for checkboxes). For basic note display, strip the ENML wrapper and convert to plain HTML by replacing en-note with div, removing the XML declaration and DOCTYPE, and replacing or removing en-media elements. The strip-tags or cheerio npm packages work well for this parsing. For production apps where formatting fidelity matters, the evernote npm package includes enml-to-html conversion utilities. Inline images in Evernote notes are stored as resources (attachments) and referenced in ENML by their hash. To display images, you need to fetch the resource data separately using noteStore.getResourceByHash() and either return the binary data as a base64 data URL or proxy it through your API route. This is the most complex part of Evernote rendering and is often skipped in dashboard-style integrations where note summaries are sufficient. For search within note content, rely on Evernote's server-side full-text search rather than fetching all content and searching client-side. Evernote maintains search indexes for all note text including OCR'd content from images — a feature that is difficult to replicate locally.
Create a Next.js API route at app/api/evernote/notes/[guid]/route.ts that fetches and renders a single note. Use getEvernoteClient from lib/evernote.ts to get the noteStore, then call noteStore.getNote(guid, true, false, false, false) to get the note with content. The content is ENML (XML). Convert it to HTML: remove the XML declaration and DOCTYPE lines, replace <en-note ...> with <div class='note-content'>, replace </en-note> with </div>, remove all en-media tags (replace with '[Attachment]' placeholder), and replace en-todo with checkbox inputs. Return { guid, title, content: htmlString, created, updated, notebookGuid }.
Paste this in Bolt.new chat
1// app/api/evernote/notes/[guid]/route.ts2import { NextResponse } from 'next/server';3import { getEvernoteClient } from '@/lib/evernote';45function enmlToHtml(enml: string): string {6 if (!enml) return '';78 return enml9 // Remove XML declaration10 .replace(/<\?xml[^>]*\?>/gi, '')11 // Remove DOCTYPE12 .replace(/<!DOCTYPE[^>]*>/gi, '')13 // Replace en-note opening tag14 .replace(/<en-note[^>]*>/gi, '<div class="note-content">')15 // Replace en-note closing tag16 .replace(/<\/en-note>/gi, '</div>')17 // Replace en-media with placeholder18 .replace(/<en-media[^/]*/gi, '<span class="attachment-placeholder">[Attachment]')19 .replace(/\/>/gi, '</span>')20 // Replace en-todo checkboxes21 .replace(/<en-todo\s+checked="true"\s*\/>/gi, '<input type="checkbox" checked disabled />')22 .replace(/<en-todo[^/]*\/>/gi, '<input type="checkbox" disabled />')23 // Clean up any remaining Evernote namespace attributes24 .replace(/\s+xmlns:[^"]*"[^"]*"/gi, '')25 .trim();26}2728export async function GET(29 _request: Request,30 { params }: { params: { guid: string } }31) {32 try {33 const client = getEvernoteClient();34 const noteStore = client.getNoteStore();3536 // getNote(guid, withContent, withResourcesData, withResourcesRecognition, withResourcesAlternateData)37 // @ts-expect-error - Evernote SDK types are loose38 const note = await noteStore.getNote(params.guid, true, false, false, false);3940 const html = enmlToHtml(note.content || '');4142 return NextResponse.json({43 guid: note.guid,44 title: note.title,45 content: html,46 created: note.created,47 updated: note.updated,48 notebookGuid: note.notebookGuid,49 tagNames: note.tagNames || [],50 });51 } catch (err) {52 const message = err instanceof Error ? err.message : 'Failed to fetch note';53 return NextResponse.json({ error: message }, { status: 500 });54 }55}Pro tip: Evernote's ENML can contain arbitrary HTML-like content including inline styles and complex nested tables. If the converted HTML looks broken or poorly styled, add a CSS reset class (max-width, line-height, font-family) around the note content container to normalize the rendering. The note-content div class added in the converter is a good hook for targeted styles.
Expected result: The note detail route fetches a specific note by GUID and returns its content as a clean HTML string. Notes with basic paragraph formatting should render correctly. Notes with attachments will show the placeholder text where attachment media elements appeared.
Handle OAuth 1.0a for Multi-User Apps (Requires Deployment)
Handle OAuth 1.0a for Multi-User Apps (Requires Deployment)
If you want users to log in to authorize your app to access their own Evernote accounts, OAuth 1.0a is required. This is a more complex protocol than OAuth 2.0, involving multiple round-trips with cryptographic signatures on each request. The OAuth 1.0a flow with Evernote has four steps. First, your server requests a temporary token from Evernote by making a signed POST to https://www.evernote.com/oauth. Second, you redirect the user to https://www.evernote.com/OAuth.action?oauth_token=TEMP_TOKEN where they see an Evernote authorization screen. Third, after the user approves, Evernote redirects to your callback URL with oauth_token and oauth_verifier query parameters. Fourth, your server exchanges the verifier for a permanent access token. Step three — receiving the OAuth callback — is the critical requirement for deployment. The Bolt WebContainer preview URL (a hash-based *.webcontainer-api.io URL) cannot be registered as an OAuth callback and cannot receive incoming HTTP redirects from Evernote. You must deploy to Netlify or Bolt Cloud first, then register your deployed URL as the OAuth callback in your Evernote API key settings at dev.evernote.com. The oauth-1.0a npm package simplifies signature generation. Install it with npm install oauth-1.0a crypto-js and use it to sign each request with your consumer key, consumer secret, and the appropriate token for each step. Store the temporary token in a server-side session (or a signed cookie) between the redirect and the callback to maintain state. For personal apps accessing only your own notes, skip OAuth entirely and use the developer token approach from Step 1 — it is much simpler. OAuth 1.0a is only necessary when other Evernote users need to log in to your app.
Add a comment in the codebase explaining that Evernote OAuth 1.0a requires deployment to receive callbacks. Create placeholder API routes for the OAuth flow: /api/evernote/auth/start that would initiate the OAuth 1.0a flow by requesting a temporary token, and /api/evernote/auth/callback that would receive the oauth_verifier. Add TODO comments explaining each step. For now, both routes should return a JSON response explaining that OAuth setup requires deployment to Netlify or Bolt Cloud and a registered callback URL at dev.evernote.com.
Paste this in Bolt.new chat
1// app/api/evernote/auth/start/route.ts2// IMPORTANT: Evernote OAuth 1.0a requires a publicly accessible callback URL.3// This endpoint will not work in Bolt's WebContainer preview.4// Deploy to Netlify or Bolt Cloud first, then:5// 1. Register your deployed URL + '/api/evernote/auth/callback' in Evernote developer portal6// 2. Add EVERNOTE_CONSUMER_KEY and EVERNOTE_CONSUMER_SECRET to your hosting environment variables7// For personal use, skip OAuth entirely and use a developer token from dev.evernote.com8import { NextResponse } from 'next/server';910export async function GET() {11 const isDevelopment = process.env.NODE_ENV === 'development';1213 if (isDevelopment) {14 return NextResponse.json({15 error: 'OAuth flow requires deployment',16 message: 'Evernote OAuth 1.0a callbacks cannot be received in Bolt WebContainer preview. Deploy to Netlify or Bolt Cloud first, then register your callback URL at dev.evernote.com.',17 alternative: 'For personal apps, use a developer token from dev.evernote.com instead of OAuth.',18 }, { status: 501 });19 }2021 // TODO: Implement OAuth 1.0a flow after deployment22 // 1. Generate oauth_nonce and oauth_timestamp23 // 2. Sign request with consumer key + secret using HMAC-SHA124 // 3. POST to https://www.evernote.com/oauth to get temp token25 // 4. Redirect user to https://www.evernote.com/OAuth.action?oauth_token={tempToken}26 return NextResponse.json({ message: 'OAuth initiation - implement after deployment' });27}Pro tip: Consider recommending Notion instead of Evernote for new multi-user apps. Notion uses OAuth 2.0 (far simpler), returns JSON content (no ENML parsing), and has a more modern developer experience. Evernote's OAuth 1.0a and ENML format are legacy constraints that add significant complexity. Evernote is best for apps specifically serving existing heavy Evernote users with years of data already in the platform.
Expected result: The OAuth start route is in place as a placeholder. During development, it returns a helpful message explaining the deployment requirement. After deploying and registering the callback URL, the TODO implementation steps guide the full OAuth 1.0a flow completion.
Common use cases
Personal Note Library Dashboard
Build a custom view of your own Evernote library using a developer token. Show notebooks and their note counts, display recent notes with titles and creation dates, and provide a search interface for finding notes by keyword. This personal dashboard can focus on the specific workflow metadata you care about without Evernote's full interface complexity.
Build a personal Evernote dashboard using a developer token. Create /api/evernote/notebooks that fetches all my notebooks from the Evernote API (listNotebooks). Create /api/evernote/notes that accepts query params: notebookGuid (optional), search (optional text query), maxNotes (default 20). Use findNotesMetadata to return note titles, creation dates, notebook names, and tag names without fetching full content. Build a React dashboard with a notebook sidebar and a note list in the center panel showing note title, notebook, creation date, and a snippet. Add a search bar that filters using Evernote's search grammar. Store EVERNOTE_DEVELOPER_TOKEN and EVERNOTE_NOTE_STORE_URL in process.env.
Copy this prompt to try it in Bolt.new
Note Content Viewer with ENML Rendering
Build a note reader that fetches and displays the full content of Evernote notes, converting ENML to readable HTML. Show the note body with basic formatting — paragraphs, headings, lists, and links — and handle inline images by fetching attachment resources separately. This enables a custom reading interface with exactly the typography and layout you want.
Create a note viewer page that displays full Evernote note content. Build /api/evernote/notes/[guid] that fetches the note using getNoteContent. The ENML content is XML — strip the ENML wrapper and Evernote-specific tags to produce clean HTML, replacing <en-note> with <div> and removing <en-media> tags (or replacing with placeholder text for attachments). Return { title, content: htmlString, created, updated, tags }. Build a React note detail page that renders the HTML with dangerouslySetInnerHTML inside a max-w-prose container with good typography.
Copy this prompt to try it in Bolt.new
Tag-Based Knowledge Base View
Build a structured knowledge base view on top of tagged Evernote notes. Notes organized with a consistent tagging system become queryable topics — fetch all notes with a specific tag to build topic pages, show a tag cloud for navigation, and surface related notes by shared tags. This turns an existing Evernote library into a searchable internal wiki.
Build a tag-based knowledge browser for Evernote. Create /api/evernote/tags that calls listTags to return all tags with names. Create /api/evernote/notes-by-tag that accepts tagGuid and returns notes with that tag using findNotesMetadata with a NoteFilter setting tagGuids. Build a React page with a tag cloud on the left (larger text for tags with more notes) and a note list on the right that updates when a tag is clicked. Each note in the list shows title, creation date, and notebook name.
Copy this prompt to try it in Bolt.new
Troubleshooting
EDAMUserException or EDAMNotFoundException when calling Evernote API methods
Cause: The most common causes are an incorrect developer token, using the wrong Note Store URL for your account shard, or attempting to access a note or notebook that does not exist or has been deleted.
Solution: Verify your developer token and Note Store URL match exactly as shown on dev.evernote.com for your account. Confirm the note or notebook GUID exists by first calling listNotebooks and comparing GUIDs. If you recently deleted a note, use findNotes with includeDeleted: true to confirm it is in the trash rather than permanently deleted.
ENML content renders with broken layout or no formatting in the browser
Cause: ENML conversion removed too many tags or improperly handled nested structures. Some ENML notes contain complex table layouts, nested divs with inline styles, or custom Evernote formatting that does not translate cleanly to plain HTML.
Solution: Use the cheerio npm package for more robust ENML parsing. Load the ENML string with cheerio, remove en-media and en-todo elements individually, rename en-note to div, and export the result as HTML. Cheerio handles malformed XML more gracefully than simple string replacement. Add CSS normalization to the note content container.
1// Using cheerio for more robust ENML conversion:2import * as cheerio from 'cheerio';34function enmlToHtmlSafe(enml: string): string {5 const $ = cheerio.load(enml, { xmlMode: true });6 $('en-media').replaceWith('<span class="attachment">[Attachment]</span>');7 $('en-todo[checked="true"]').replaceWith('<input type="checkbox" checked disabled />');8 $('en-todo').replaceWith('<input type="checkbox" disabled />');9 const content = $('en-note').html() || '';10 return `<div class="note-content">${content}</div>`;11}OAuth callback receives oauth_token and oauth_verifier but token exchange fails
Cause: OAuth 1.0a requires the temporary token stored during the initiation step to be available during the callback. If you are using stateless serverless functions without a session store, the temporary token is lost between the redirect and the callback.
Solution: Store the temporary token and its secret in an encrypted cookie during the OAuth initiation step. In the callback handler, read the cookie to retrieve the temporary token, then use it alongside the verifier to request the final access token. Alternatively, encode the temporary token in the callback URL as a state parameter (less secure but simpler for development).
Evernote API calls time out or respond very slowly for large accounts
Cause: Accounts with tens of thousands of notes can have slow listNotebooks and findNotesMetadata responses, especially the first call after a long period of inactivity as Evernote's API servers warm up the session.
Solution: Add aggressive caching — 5 minutes for notebooks (they rarely change), 30 seconds for note lists. Implement a loading state in the UI with a timeout message. Consider fetching notebooks and notes in parallel using Promise.all() rather than sequentially. For very large accounts, use the Evernote API's maxNotes parameter to limit initial results to 10-20 most recent notes.
1// Parallel fetch for initial page load:2const [notebooksResult, recentNotesResult] = await Promise.all([3 fetchNotebooks(),4 fetchNotesMeta({ maxNotes: 20 }),5]);Best practices
- Use developer tokens for personal apps accessing your own notes — avoid OAuth 1.0a complexity unless you are building an app for other Evernote users to log into.
- Never expose EVERNOTE_DEVELOPER_TOKEN or OAuth credentials with the NEXT_PUBLIC_ prefix — these tokens grant full read and write access to all notes in the account.
- Cache Evernote API responses aggressively (notebooks for 5 minutes, note lists for 30 seconds) — Evernote's API can be slow, and notes rarely change in real time during a browser session.
- For multi-user OAuth apps, deploy to Netlify or Bolt Cloud before registering the callback URL — the Bolt WebContainer preview cannot receive OAuth callbacks from Evernote.
- Handle ENML conversion defensively — notes created in different Evernote clients (mobile, web, desktop) can have varying ENML structures; always sanitize output with a library like DOMPurify before rendering with dangerouslySetInnerHTML.
- Consider Notion as a modern alternative for new note-based integrations — Notion's OAuth 2.0 and JSON block format are significantly easier to work with than Evernote's OAuth 1.0a and ENML.
- Use Evernote's server-side search (via the words parameter in NoteFilter) rather than fetching all notes and filtering client-side — Evernote's search is fast, full-text, and includes OCR'd text from images.
- Respect the pagination parameters in findNotesMetadata — fetching thousands of note metadata records at once is slow and wasteful; limit to 20-50 per page and implement client-side pagination.
Alternatives
Notion uses OAuth 2.0 and returns JSON block content rather than XML, making it significantly easier to integrate; choose Evernote only if your users specifically have years of data stored in Evernote they cannot migrate.
Google Docs API uses OAuth 2.0 and returns structured document content; it is better for collaborative document workflows while Evernote is stronger for personal note organization with tags and notebooks.
Todoist is better for task and checklist management with a modern REST API; Evernote is better when the primary need is accessing long-form written notes and document content.
OmniFocus is a powerful task management tool for Apple platforms; Evernote is better when the content is notes and documents rather than structured tasks and projects.
Frequently asked questions
Can I use the Evernote API in Bolt's WebContainer preview?
Yes — outbound Evernote API calls using a developer token work in Bolt's WebContainer preview. You can list notebooks, fetch note metadata, and retrieve note content during development. The only scenario requiring deployment is OAuth 1.0a callback handling for multi-user apps, since the WebContainer preview URL cannot receive incoming HTTP redirects from Evernote's authorization servers.
Does Bolt.new have a native Evernote integration?
No — Bolt.new does not have a built-in Evernote connector. The integration uses Evernote's API directly with the official evernote npm package. Bolt's AI can generate the integration code, but the setup is more complex than most integrations due to Evernote's OAuth 1.0a protocol and ENML content format.
What is the difference between a developer token and an API key for Evernote?
A developer token grants API access to your own personal Evernote account — it is tied to your specific account and can only read and write your own notes. An API key (consumer key and consumer secret) is for building apps that other Evernote users authorize to access their own accounts via OAuth 1.0a. For personal productivity tools accessing only your own data, use a developer token. For apps where other users log in with their Evernote accounts, use an API key with OAuth.
Why does Evernote use ENML instead of standard HTML for note content?
ENML (Evernote Markup Language) is a subset of XHTML that Evernote designed to be a portable, safe format for note content that works consistently across all Evernote clients (iOS, Android, Web, Desktop). It includes Evernote-specific elements like en-media for attachments and en-todo for checkboxes. While this format made sense when Evernote was designed, it adds complexity for API integrations compared to modern APIs that return JSON or Markdown.
How do I display images and attachments embedded in Evernote notes?
Images and attachments in Evernote notes are stored as resources referenced by hash in the ENML. To display them, call noteStore.getResource(hash, withData: true) to get the binary data, then convert it to a base64 data URL. For API routes, you can return the binary as a stream response or pre-convert to base64 in the note fetch route. This adds complexity — for most use cases, replacing en-media elements with placeholder text is sufficient.
Is Evernote a good choice for a new project or should I use something else?
Evernote integration makes the most sense when your users specifically have existing data in Evernote they want to access through your app. For new projects where you are choosing the data backend, Notion offers a far more developer-friendly API with OAuth 2.0, JSON responses, and no XML content format to deal with. Evernote is best for serving users who already have years of notes stored in the platform.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation