← barua.tz

Your database#

Bring your own Postgres and your mail lives there, not on Barua. Neon, Supabase, Railway, RDS, a server of your own: anything Postgres 13 or newer that Barua can reach over the internet with TLS. Connect it once and Barua creates a schema called barua with four tables, then writes to them as things happen: every email you send, with its subject, html and text; each delivery outcome; every message received on your domains; and the files attached to those messages.

Barua keeps only what it needs to protect your sending reputation and answer GET /emails: addresses, status and timestamps. The content is yours only. Your database is yours to query, join, back up and keep, and to walk away with.

barua.emailsEvery email sent through the API: subject, html and text, who it went to, and what became of it.
barua.eventsEach delivery outcome as it happened: delivered, bounced, deferred, complained.
barua.inboundEvery message received on your domains: headers, text and html.
barua.attachmentsThe files attached to received messages, as bytes.
a query to start with
select from_address, subject, received_at
from barua.inbound
order by received_at desc
limit 20;

How it holds up: a write that fails because your database is unreachable is queued on Barua and retried with growing gaps for up to seven days, so nothing is lost while your database is down for an evening. Each received message also raises a Postgres notification on channel barua; LISTEN barua in your app wakes the moment one lands.

A sub-account uses its parent's database unless it has one of its own, which is how an integrator gives each customer their own. The URL is stored encrypted and never shown again. Barua connects only to public hosts; private and loopback addresses are refused, and so is sslmode=disable. sslmode=no-verify is accepted for a server with a self-signed certificate.

GET /api/v1/database

Read the connected database. Scope database:read.

Where it is, whether Barua last reached it, and how many writes are waiting on Barua because it could not. Always 200: with no database set, connected is false and the rest is empty.

curl
curl https://barua.tz/api/v1/database \
  -H "Authorization: Bearer barua_YOUR_KEY"
response 200
200
{
  "connected": true,
  "host": "ep-quiet-lake-123.eu-central-1.aws.neon.tech",
  "port": 5432,
  "database": "neondb",
  "user": "app",
  "sslMode": "verify",
  "state": "connected",
  "schemaVersion": 1,
  "lastOkAt": "2026-09-24T10:21:00.000Z",
  "lastError": null,
  "updatedAt": "2026-09-24T09:00:00.000Z",
  "pending": 0
}

POST /api/v1/database

Connect your own Postgres. Scope database:write.

Barua connects to the URL, creates a schema called barua there with its tables, and stores the URL encrypted. From then on every email sent, each delivery outcome, every message received on your domains and its attachments are written to it. Connecting again replaces the stored URL; the old database is left as it is. Private, loopback and reserved hosts are refused, and so is sslmode=disable; use sslmode=no-verify for a server with a self-signed certificate. The URL is never returned.

curl
curl https://barua.tz/api/v1/database \
  -H "Authorization: Bearer barua_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "postgresql://app:secret@ep-quiet-lake-123.eu-central-1.aws.neon.tech/neondb?sslmode=require"
  }'
response 200
200
{
  "connected": true,
  "host": "ep-quiet-lake-123.eu-central-1.aws.neon.tech",
  "port": 5432,
  "database": "neondb",
  "user": "app",
  "sslMode": "verify",
  "state": "connected",
  "schemaVersion": 1,
  "lastOkAt": "2026-09-24T10:21:00.000Z",
  "lastError": null,
  "updatedAt": "2026-09-24T09:00:00.000Z",
  "pending": 0
}
400 invalid_jsonThe request body could not be parsed as JSON.
422 invalid_requestThe body or query failed validation. The message names the first field that failed.
422 invalid_urlThe URL cannot be used. A webhook URL must be https, carry no credentials and point at a public host that resolves. A database URL must be postgresql:// with a host and a user, and may not turn TLS off with sslmode=disable.
422 blocked_hostThe host resolves to a private, loopback or reserved address. Barua connects only to public hosts.
422 database_unreachableBarua could not connect, log in, or create its schema there. The message says which.
503 not_configuredThe server cannot store secrets yet, so neither a webhook secret nor a database URL can be kept.

POST /api/v1/database/test

Check the connection now. Scope database:write.

Connects to the stored URL and reports what happened, so a rotated password or a moved server shows up here rather than on the next write. The status returned is fresh: state, lastOkAt and lastError all reflect this attempt.

curl
curl -X POST https://barua.tz/api/v1/database/test \
  -H "Authorization: Bearer barua_YOUR_KEY"
response 200
200
{
  "connected": true,
  "host": "ep-quiet-lake-123.eu-central-1.aws.neon.tech",
  "port": 5432,
  "database": "neondb",
  "user": "app",
  "sslMode": "verify",
  "state": "connected",
  "schemaVersion": 1,
  "lastOkAt": "2026-09-24T10:21:00.000Z",
  "lastError": null,
  "updatedAt": "2026-09-24T09:00:00.000Z",
  "pending": 0
}
404 database_not_connectedNo database is set on this account.
422 database_unreachableBarua could not connect, log in, or create its schema there. The message says which.
422 blocked_hostThe host resolves to a private, loopback or reserved address. Barua connects only to public hosts.

DELETE /api/v1/database

Disconnect it; nothing in your database is touched. Scope database:write.

Barua forgets the URL and stops writing. The barua schema and every row in it stay where they are, because they are yours.

curl
curl -X DELETE https://barua.tz/api/v1/database \
  -H "Authorization: Bearer barua_YOUR_KEY"
response 204
204, no body
404 database_not_connectedNo database is set on this account.