Skip to main content

PostgreSQL Database

Every Deepline workspace gets its own dedicated Neon PostgreSQL project. Not a shared multi-tenant database. Not a proprietary data store you need a vendor SDK to query. A real PostgreSQL database with your own schemas, your own roles, and direct SQL access from any client that speaks the Postgres wire protocol. Every GTM team eventually asks: “Where does my data actually live, and can I get it out?” Yes. It lives in PostgreSQL. You can get it out with psql, DBeaver, Metabase, your ORM, a pg driver, or a COPY TO command. There is no export queue, no CSV-only download button, no 10,000-row limit.
AttributeValue
Database enginePostgreSQL (Neon serverless)
IsolationOne dedicated Neon project per tenant
SchemasManaged schemas + agent-writable storage schema
Database rolesWorkspace-scoped customer DB and internal runtime roles
Schema migrationsRolling migrations supported
Direct SQL accessAny PostgreSQL client (psql, DBeaver, Metabase, pg driver)
Data exportUnrestricted — pg_dump, COPY TO, direct query
Data sovereigntyYour data stays in your schema

What an included database means for your GTM data

Most enrichment tools store your data in their cloud and give you a UI to look at it. If you want to do anything real with that data — join it to your CRM, run custom reports, build internal tools, deduplicate across providers — you are either exporting CSVs or paying for an API that rate-limits you against your own records. Deepline takes a different approach. Your enrichment results, identity graph, manual overrides, and resolved views all live in a PostgreSQL database that you can connect to directly. Here is when that matters and when it does not.

Data sovereignty

Your enrichment results live in your schema, not in a vendor’s multi-tenant database. Query, export, back up, or delete your data at any time with standard PostgreSQL tools. No vendor lock-in. No export fees. No “please contact sales to download your records.” If you leave Deepline, your data is a pg_dump away.

Custom reporting

Connect Metabase, Looker, Mode, Grafana, or any BI tool directly to your database. Write SQL to answer questions the vendor UI never anticipated: “Which companies in our ICP had job postings in the last 30 days but no enriched contact?” “What is our email verification rate by provider over time?” Full SELECT access to resolved views — no API pagination, no row limits.

Identity resolution

Enrich the same person through Apollo, Hunter, and People Data Labs and you get three separate responses with overlapping but inconsistent data. The identity graph (dl_graph) links enrichment events to resolved entities, deduplicates across providers, and produces a single coalesced record in dl_resolved. Query the resolved view and get the best available data without writing merge logic.

Not relevant for ephemeral lookups

If you run a single enrichment and pipe the result straight into a CRM or spreadsheet, you may never touch the database directly. The CLI and API return results inline. The database matters when you build on top of enrichment data over time — running waterfalls, deduplicating contacts, tracking history, or building internal tools.

Agent access with query_customer_db

Agents normally access the workspace database through query_customer_db, not by holding an owner connection string. The tool accepts one SQL statement and returns structured rows. Use it for reads:
deepline tools execute query_customer_db --payload '{
  "sql": "SELECT domain, company_name FROM enrichments.companies ORDER BY updated_at DESC LIMIT 20"
}'
Use the storage schema for agent-created tables and scratch state:
deepline tools execute query_customer_db --payload '{
  "sql": "CREATE TABLE IF NOT EXISTS storage.agent_notes (id text PRIMARY KEY, note text NOT NULL, created_at timestamptz NOT NULL DEFAULT now())"
}'
query_customer_db can run SELECT, EXPLAIN, read-only WITH, and storage-scoped writes. Write statements must name the target as storage.<table> or "storage"."<table>". Unqualified DDL such as CREATE TABLE agent_notes (...) is blocked.

Schema architecture

Each tenant database contains managed schemas plus an agent-writable storage schema. The schemas are designed around a clear data flow: raw enrichment results land in managed storage, identity resolution links records to resolved entities, coalesced views surface read models, and platform metadata tracks schema versioning and tenant configuration.

The 5 managed schemas

Stores raw enrichment events from every provider call. Each row represents a single enrichment response keyed by a unique source identifier (provider + operation + input hash). The doc column holds the full JSON response from the upstream provider. The extracted_potential_identifier_keys column contains parsed identifiers (emails, LinkedIn URLs, domains) used by the identity graph for entity linking.Key table: dl_cache.enrichment_eventThis schema is write-only from the application perspective. The platform runtime role writes enrichment results here; tenant roles have no direct write access. You can read cached enrichment data through the dl_resolved views, which merge cache and override data into a single coalesced record.Why it matters: Every enrichment call you have ever made is stored here. You never re-pay for data you already have. The cache also powers waterfall logic — if Provider A already returned an email for this person, Provider B is only called if the cached result is stale or incomplete.
Stores human-entered corrections and custom enrichment events. When a user corrects an email, updates a job title, or tombstones a stale record, the change lands here. Override records take precedence over cached enrichment data when the dl_resolved views are queried.Key table: dl_override.custom_enrichment_eventThis schema is platform-managed. Do not write to it through query_customer_db; use the supported product/API workflow for corrections when one is available.Why it matters: Enrichment data is never perfect. Overrides give you a clean separation between “what the provider said” and “what we know is actually correct,” without mutating the original cached data.
The core of cross-provider deduplication. This schema contains three interconnected tables:
  • dl_graph.entities — Resolved person and company entities. Each entity has a type (person or company) and an optional parent link (person → company).
  • dl_graph.adoptions — Links enrichment event rows (from dl_cache or dl_override) to their resolved entity. Tracks confidence scores and the reasoning behind each adoption.
  • dl_graph.identifier_memberships — Maps parsed identifiers (email addresses, LinkedIn URLs, company domains, phone numbers) to entities. This is the join table that enables “find all enrichment results for this person across all providers.”
Why it matters: When you enrich the same VP of Sales through three different providers, you get three separate JSON blobs. The identity graph resolves them into a single entity, links all three enrichment events to that entity, and makes the merged result available through dl_resolved. Without the graph, you would be writing custom deduplication logic for every provider combination.
Read-only views that merge dl_cache enrichment events, dl_override corrections, and dl_graph entity resolution into a single queryable surface. This is the schema you query 90% of the time.Key views:
  • dl_resolved.people — One row per resolved person entity, with the best available data from all linked enrichment events and any override patches applied.
  • dl_resolved.companies — Same for companies.
  • dl_resolved.coalesced_enrichment_event — Lower-level view showing individual enrichment events with override patches applied.
Why it matters: You never have to write merge logic. Query dl_resolved.people and you get a single record per person with the best email, most recent job title, and all linked identifiers — regardless of which provider originally returned each field.
Tracks schema version and tenant-specific configuration. The platform uses this to manage rolling migrations across tenant databases.Key tables:
  • dl_meta.schema_migrations — Ordered list of applied migrations with timestamps. Used by the migration runner to determine which migrations need to run on each tenant.
  • dl_meta.tenant_settings — Key-value store for tenant-specific configuration (e.g., which schema object names are active after a migration).
Why it matters: Rolling migrations mean your database schema evolves without downtime. The migration runner checks dl_meta.schema_migrations, applies any pending migrations in order, and updates dl_meta.tenant_settings with the new configuration. You do not need to manage migrations yourself — this is platform-managed.

The agent-writable schema

In addition to managed schemas, every tenant database includes a storage schema. The customer DB role can create, alter, insert, update, delete, and drop objects in this schema when statements schema-qualify the target. Use it for agent-created tables, scratch state, workflow notes, and small application objects that live alongside enrichment data.
CREATE TABLE IF NOT EXISTS storage.account_segments (
  id text PRIMARY KEY,
  segment text NOT NULL,
  score numeric,
  created_at timestamptz NOT NULL DEFAULT now()
);

Database roles

Each tenant database has carefully scoped permissions. You never connect as the owner role; it is used only during provisioning and migrations.
RolePurposePermissions
OwnerProvisioning and DDLFull access to all schemas. Used by the platform during bootstrap and migrations. Never exposed to users.
Customer DBAgent and app SQLRead access to permitted workspace schemas, plus DDL and DML on schema-qualified storage.* tables.
Runtime (dl_platform_runtime)Platform operationsInternal role for enrichment writes, identity graph updates, and managed runtime work.
Which path should I use? Start with query_customer_db for agent-driven reads and storage-scoped writes. Use direct connection URIs only when an app or BI tool needs a PostgreSQL connection string.

How to access your database

1. Check workspace status

curl -s https://code.deepline.com/api/v2/ingestion/status \
  -H "Authorization: Bearer $DEEPLINE_API_KEY" | jq .
If not provisioned, provision first from the dashboard Database Access flow (workspace admin session required):
POST /api/v2/ingestion/provision

2. Get a connection URI

POST /api/v2/ingestion/connection-uris/reveal
{
  "kind": "customer_db",
  "pooled": true
}
The customer DB URI can read permitted workspace schemas and write to schema-qualified storage.* tables.
GET /api/v2/ingestion/status supports API key auth. Provisioning and credential reveal/rotation flows are currently intended for the dashboard/admin session.

3. Connect with any PostgreSQL client

psql "postgresql://dl_ingest_customer_db:****@ep-xxxxx.us-east-2.aws.neon.tech/neondb?sslmode=require"

4. Export your data

Full database export:
pg_dump "postgresql://dl_ingest_customer_db:****@ep-xxxxx.us-east-2.aws.neon.tech/neondb?sslmode=require" \
  --schema=dl_resolved \
  --no-owner \
  -f deepline-export.sql
Export to CSV:
psql "$DEEPLINE_DB_URI" -c "\copy (SELECT * FROM dl_resolved.people) TO 'people.csv' WITH CSV HEADER"

Rolling migrations

Deepline uses rolling migrations to evolve your database schema without downtime. Each migration is versioned, ordered, and idempotent. The migration runner:
  1. Reads dl_meta.schema_migrations to determine the current schema version
  2. Applies any pending migrations in order
  3. Updates dl_meta.tenant_settings with new configuration (e.g., renamed tables/views)
  4. Verifies the migration with SQL checks
You do not need to run migrations yourself. The platform manages migration rollout across all tenant databases. Migrations are designed to be backward-compatible during the rollout window — your queries against dl_resolved continue to work while migrations are in progress.

Frequently asked questions

Yes. Request a customer_db connection URI and configure your BI tool with the host, port, database, username, and password from the URI. SSL is required. Use resolved or enrichment views as the primary query surface for reporting.
No. Each workspace gets a dedicated Neon project. Your data is physically isolated — separate compute, separate storage, separate connection endpoints. There is no row-level security or shared-schema multi-tenancy.
Yes, through query_customer_db or the customer DB role when every write target is schema-qualified under storage. For example: CREATE TABLE IF NOT EXISTS storage.agent_notes (...). Unqualified DDL such as CREATE TABLE agent_notes (...) is blocked, as are writes to managed schemas.
Call the credential rotation endpoint:
POST /api/v2/ingestion/connection-uris/rotate
{
  "kind": "customer_db"
}
This generates a new password for the specified role. Existing connections using the old password will be terminated.
Yes. Connect with the customer DB role and run pg_dump against schemas that role can read. For a full export of resolved data, dump the read-model schema available in your workspace. For agent-created state, dump storage.
Neon runs PostgreSQL 16. All standard PostgreSQL 16 features, extensions (pgcrypto is enabled by default), and wire protocol compatibility apply.
Neon’s serverless architecture scales storage automatically. There is no artificial row limit imposed by Deepline. Storage costs are part of the Neon project provisioned for your workspace — see the pricing page for details.

Database Access (Developer Guide)

API endpoints, code patterns, and the full schema contract reference for building on your database.

Quick Start

Install Deepline and run your first enrichment in 60 seconds.

CLI Concepts

Command patterns, payloads, and execution flow.

Pricing

BYOK free tier, managed credits, and what is included with your database.