uppinote201k downloadsAutomatically import notes from an external database like Airtable into your Vault.
Import and sync notes bidirectionally between Airtable, SeaTable, and your Obsidian vault with smart field mapping and organization features. Built on a provider-agnostic core; more databases (Supabase, Notion, Custom API) tracked in #11.
Manual, Obsidian wins, Remote wins modes{{fieldName}} placeholders with nested-property accessdata.records:read — required for importingdata.records:write — required for bidirectional syncschema.bases:read — required for field selectionhttps://cloud.seatable.io; self-hosted users use their own host)https://abc.supabase.co)sb_publishable_…) — RLS-protected, safe for client-side use. Recommended.anon (JWT) — RLS-protected, also fine but deprecated by Supabasesb_secret_…) or service_role (JWT) — bypasses RLS; only use if you understand the implications. The plugin auto-detects key type and warns when a secret key is entered.public is already exposed)anon JWT and secret keys don't need this step.{{fieldName}} templateYou can have multiple configurations (e.g. one for Airtable, one for SeaTable, or two SeaTable bases) — switch between them with the tab bar at the top of the settings panel.
Use Command Palette (Ctrl/Cmd + P). Each command is labeled with the active config's provider:
| Command | Description |
|---|---|
| Sync current note from {provider} | Refresh current note from the remote database |
| Sync all notes from {provider} | Import / update all notes |
| Sync current note to {provider} * | Push current note changes |
| Sync modified notes to {provider} * | Push pending changes |
| Sync all notes to {provider} * | Push every note |
| Bidirectional sync current note * | Push, wait for formulas, then pull |
| Bidirectional sync modified notes * | Same for modified notes |
| Bidirectional sync all notes * | Same for all notes |
* Commands marked with * require Enable bidirectional sync to be turned on. They are hidden from Command Palette when disabled.
You can also schedule syncs:
| Setting | Description |
|---|---|
| Credential | Pick a registered credential (Airtable / SeaTable) |
| Base / Table / View (Airtable) | Selectable from your base via the Meta API |
| Table / View ID (SeaTable) | Identifiers from your SeaTable base — auto-derived dropdowns are tracked in #73 |
| Filename Field | Field used for note filenames (safe types only) |
| Subfolder Field | Optional — organize notes into subfolders |
| New File Location | Destination folder in your vault |
| Template File | Custom template (optional) |
| Sync Interval | Auto-sync frequency in minutes (0 = disabled) |
| Allow Overwrite | Update existing notes vs skip duplicates |
| Setting | Description |
|---|---|
| Enable bidirectional sync | Allow Obsidian → remote pushes |
| Conflict resolution | Manual, Obsidian wins, Remote wins |
| Watch for file changes | Auto-detect Obsidian edits and queue sync |
| Auto-sync computed fields | After push, fetch formulas / rollups / lookups |
| Computed-field sync delay | ms to wait for the remote to recompute (default: 1500) |
The plugin maps each provider's native types to a normalized taxonomy (text / number / date / boolean / single-select / multi-select / attachment / link / computed / system). Each provider's FieldTypeMapper decides which types are filename-safe and which are read-only (excluded from push) — fail-closed for unknown types.
✅ Safe for Filenames & Subfolders: singleLineText, singleSelect, number, formula
🔒 Read-only (synced from Airtable only): formula, rollup, count, lookup, externalSyncSource, aiText, button, createdTime, lastModifiedTime, createdBy, lastModifiedBy, autoNumber
📋 Complete Airtable Field Type Reference →
✅ Safe for Filenames & Subfolders: text, single-select, number, auto-number, formula
🔒 Read-only (synced from SeaTable only): formula, link-formula, button, ctime, mtime, creator, last-modifier, auto-number
✅ Safe for Filenames & Subfolders: string, string:uuid, integer, integer:int64 (and their :readonly variants — typical for PostgreSQL primary keys)
🔒 Read-only (synced from Supabase only): any column flagged readOnly: true in the PostgREST OpenAPI spec — typically GENERATED ALWAYS AS ... columns and view-derived columns
PostgreSQL type → standard mapping summary: text/varchar/uuid → text · integer/numeric/real → number · boolean → boolean · date/timestamp/timestamptz → date · json/jsonb → text (raw JSON string) · text[]/int[] → multi-select · bytea → unknown (skipped)
Unsupported / read-only fields are automatically hidden in dropdowns to prevent push errors.
Each note carries the remote record id in the primaryField frontmatter key — the immutable handle the sync pipeline uses to match notes back to their remote row.
destination/field-value/note.mddestination/note.md┌─────────────┐ Push ┌──────────────┐
│ Obsidian │ ───────────▶ │ Remote DB │
│ (Notes) │ │ (Airtable / │
│ │ ◀─────────── │ SeaTable) │
└─────────────┘ Pull └──────────────┘
When the same field is modified in both Obsidian and the remote:
| Mode | Behavior |
|---|---|
| Manual | Show notification, skip conflicting fields |
| Obsidian wins | Overwrite the remote with Obsidian values |
| Remote wins | Keep remote values, ignore Obsidian changes |
Create custom note templates using {{fieldName}} placeholders:
---
title: "{{Title}}"
status: "{{Status}}"
author: "{{Author.name}}"
created: "{{Created time}}"
---
# {{Title}}
## Summary
{{Summary}}
## Content
{{Description}}
## Attachments
{{Attachment.0.url}}
Advanced Features:
{{Attachment.0.url}}, {{User.name}}📝 Template Examples & Best Practices →
This plugin emits Bases-compatible YAML frontmatter with proper data types for seamless table/card view editing. Import your notes, enable the Bases plugin, and create a database from the imported folder for powerful data management workflows.
Common Issues:
data.records:write; SeaTable token "Read and write")Contributors running the Supabase e2e suite (npm run test:e2e:supabase:full) need a demo Supabase project. Create a free project at supabase.com, open the SQL Editor, and run:
CREATE TYPE note_status AS ENUM ('draft', 'published', 'archived');
CREATE TABLE notes (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
title text NOT NULL,
content text,
status note_status DEFAULT 'draft',
tags text[],
meta jsonb,
archived boolean DEFAULT false,
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now(),
full_text text GENERATED ALWAYS AS (title || ' ' || coalesce(content, '')) STORED
);
CREATE VIEW active_notes AS
SELECT * FROM notes WHERE archived = false;
ALTER TABLE notes ENABLE ROW LEVEL SECURITY;
CREATE POLICY "anon_all" ON notes FOR ALL USING (true);
If you'll run e2e (or the plugin itself) with a publishable key (sb_publishable_…), also install the schema-introspection RPC fallback — Supabase's new key system blocks the OpenAPI endpoint for publishable keys, and the plugin reads schema through this RPC instead.
Easiest path: build + load the plugin once, open Settings → Supabase Connection, and click Copy SQL in the "One-time setup required" banner. Paste into Supabase SQL Editor and Run.
For automated environments where you can't open the UI, extract the SQL with tsx (Node alone can't require a .ts file on every release):
npx tsx -e "import('./src/constants/supabase-rpc.ts').then(m => console.log(m.SUPABASE_RPC_SCHEMA_SQL))"
Re-running is safe (CREATE OR REPLACE). This step is unnecessary for legacy anon JWTs — those still receive OpenAPI directly.
Add to .env at the project root:
SUPABASE_URL=https://<your-ref>.supabase.co
SUPABASE_KEY=sb_publishable_xxxxxxxxxxxxxxxx
Then npm run test:e2e:supabase:full runs the full build + deploy + e2e flow.
Provider-specific Tips:
Server URL per credential.If you find this plugin useful, support development:
MIT License