openapi: 3.1.0
info:
title: Electric HTTP API
description: |-
[HTTP API](https://electric-sql.com/docs/sync/api/http) to sync
partial replicas of your Postgres data into local apps
and services.
See the [Electric documentation](https://electric-sql.com/docs/sync/)
for more information.
version: __PLACEHOLDER_SYNC_SERVICE_VERSION__
servers:
- url: http://localhost:3000
description: Local server
components:
parameters:
secret:
name: secret
in: query
schema:
type: string
example: 1U6ItbhoQb4kGUU5wXBLbxvNf
description: |-
Secret defined by the [ELECTRIC_SECRET](https://electric-sql.com/docs/api/config#electric-secret)
configuration variable. This is required unless
`ELECTRIC_INSECURE` is set to `true`. More details are
available in the [security guide](https://electric-sql.com/docs/guides/security).
api_secret:
name: api_secret
in: query
schema:
type: string
example: 1U6ItbhoQb4kGUU5wXBLbxvNf
deprecated: true
description: |-
Deprecated in favor of the `secret` query parameter.
Will be removed in v2.
paths:
/v1/shape:
get:
summary: Get Shape
description: |-
Load the initial data for a shape and poll for real-time updates.
Define your shape using the `table` and `where` parameters.
Use `offset` to fetch data from a specific position in the shape
log and the `live` parameter to consume real-time updates.
parameters:
# Query parameters
- name: table
in: query
schema:
type: string
examples:
simple:
value: issues
summary: the issues table in the public schema
qualified:
value: foo.issues
summary: the issues table in the foo schema
required: true
description: |-
Root table of the shape. Must match a table in your Postgres database.
Can be just a tablename, or can be prefixed by the database schema
using a `.` delimiter, such as `foo.issues`. If you don't provide
a schema prefix, then the table is assumed to be in the `public.` schema.
- name: offset
in: query
schema:
type: string
examples:
initial_sync:
value: '-1'
summary: sync the shape from the start
ongoing_sync:
value: '26800584_4'
summary: continue syncing from offset `26800584_4`
skip_historical:
value: 'now'
summary: skip all historical data and start from the current point
required: true
description: |-
The offset in the shape stream. This is like a cursor that specifies
the position in the shape log to request data from.
When making an initial request to sync a shape from scratch, you
**must** set the `offset` to `-1`. Then, when continuing to
sync data, you should set the `offset` to the last offset you have
already received, to continue syncing new data from that position
in the stream.
Alternatively, you can set `offset` to `now` to skip all historical
data and receive an immediate up-to-date message with the latest
continuation offset. This is useful when combined with `log=changes_only`
mode and `replica=full` for applications that don't need historical data.
Note that when `offset` is not `-1` or `now` then you must also provide
the shape's `handle`.
- name: live
in: query
schema:
type: boolean
description: |-
Whether to wait for live updates or not.
When the `live` parameter is omitted or set to `false`, the server
will always return immediately, with any data it has, followed by an
up-to-date message.
Once you're up-to-date, you should set the `live` parameter to `true`.
This puts the server into live mode, where it will hold open the
connection, waiting for new data arrive.
This allows you to implement a long-polling strategy to consume
real-time updates.
- name: live_sse
in: query
schema:
type: boolean
description: |-
Use Server-Sent Events (SSE) for live updates instead of long polling.
When set to `true` along with `live=true`, the server will use SSE
to stream updates to the client. SSE provides a persistent connection
that allows the server to push updates as they happen, which is more
efficient than long polling.
SSE messages are sent in the standard SSE format with `data:` prefixes.
The stream includes data messages (shape log entries), control messages
(up-to-date, must-refetch, etc.), and keep-alive comments sent every
21 seconds to prevent connection timeout.
**Important**: SSE requires that reverse proxies and CDNs support
streaming responses without buffering. Configure your proxy accordingly:
- Nginx: `proxy_buffering off;`
- Caddy: `flush_interval -1`
- Apache: `flushpackets=on`
SSE can only be enabled when `live` is also `true`.
- name: experimental_live_sse
in: query
schema:
type: boolean
deprecated: true
description: |-
Deprecated in favor of `live_sse`. Use `live_sse` instead.
This parameter will be removed in a future version.
- name: cursor
in: query
schema:
type: string
description: |-
This is a cursor generated by the server during live requests. It helps bust caches for
responses from previous long-polls.
- name: handle
in: query
schema:
type: string
example: '3833821-1721812114261'
description: |-
The shape handle returned by the initial shape request.
This is a required parameter when this is not an initial sync request,
i.e. when offset is not `-1`.
- name: where
in: query
schema:
type: string
description: |-
Optional where clause to filter rows in the `table`.
This should be a valid PostgreSQL WHERE clause using SQL syntax.
For more details on what is supported and what is optimal,
see the [where clause documentation](https://electric-sql.com/docs/sync/guides/shapes#where-clause).
If this where clause uses a positional parameter, it's value must be provided under `params[n]=`
query parameter.
examples:
title_filter:
value: |
"title = 'Electric'"
summary: Only include rows where the title is 'Electric'
status_filter:
value: |
"status IN ('backlog', 'todo')"
summary: Only include rows whose status is either 'backlog' or 'todo'
- name: params
in: query
style: deepObject
explode: true
schema:
type: object
patternProperties:
'^\d+$':
type: string
description: |-
Optional params to replace inside the where clause. Uses an "exploded object" syntax (see examples).
These values will be safely interpolated inside the where clause, so you don't need to worry about
escaping user input when building a where clause.
If where clause mentions a posisional parameter, it becomes required to provide it.
examples:
params:
value:
1: value1
summary: replace placeholder `$1` inside the where clause with `value1`
- name: columns
in: query
schema:
type: string
description: |-
Optional list of columns to sync in the rows from the `table`.
This is a projection setting for reducing the data sent to the client.
If `queryable_columns` is set, `columns` may only include columns from
that allow-list. If `queryable_columns` is set and `columns` is omitted,
Electric syncs the queryable columns by default.
They should always include the primary key columns, and should be formed
as a comma separated list of column names exactly as they are in the database schema.
If the identifier was defined as case sensitive and/or with special characters, then\
you must quote it in the `columns` parameter as well.
examples:
select_columns:
value: 'id,title,status'
summary: Only include the id, title, and status columns.
select_columns_special:
value: 'id,"Status-Check"'
summary: Only include id and Status-Check columns, quoting the identifiers where necessary.
- name: queryable_columns
in: query
schema:
type: string
description: |-
Optional list of columns that may be referenced by subset WHERE clauses,
subset ORDER BY clauses, and the `columns` projection.
This is an allow-list for what client-controlled subset requests may query
or sync. It does not force every listed column to be synced, and it does
not restrict the main shape WHERE clause.
Queryable columns should always include the primary key columns, and should
be formed as a comma separated list of column names exactly as they are in
the database schema.
examples:
queryable_columns:
value: 'id,title,org_id'
summary: Allow filtering by org_id while syncing a narrower columns projection.
- name: replica
in: query
schema:
type: string
enum:
- default
- full
description: |-
Modifies the data sent in update and delete change messages.
When `replica=default` (the default) only changed columns are
included in the `value` of an update message and only the primary
keys are sent for a delete.
When set to `full` the entire row will be sent for updates and
deletes. `old_value` will also be present on update messages,
containing the previous value for changed columns.
Note that insert operations always include the full row,
in either mode.
- name: log
in: query
schema:
type: string
enum:
- full
- changes_only
default: full
examples:
full_snapshot:
value: 'full'
summary: Load complete historical data (default)
changes_only:
value: 'changes_only'
summary: Skip historical data, only receive real-time changes
description: |-
Controls the initial data loading mode for the shape.
When `log=full` (the default), the server creates an initial snapshot
of all data matching the shape definition and streams it to the client
before delivering real-time updates.
When `log=changes_only`, the server skips the initial snapshot creation.
The client will only receive changes that occur after the shape is
established, without seeing the base data. This is useful for:
- Event streams where historical data isn't needed
- Applications that fetch their initial state through `subset__*` parameters
- Reducing initial sync time when combined with `offset=now`
In `changes_only` mode, you can use the client's `requestSnapshot` method
to fetch subsets of data on-demand while tracking which changes to skip.
- name: subset__where
in: query
schema:
type: string
examples:
status_filter:
value: "status = 'active'"
summary: Filter subset to only active records
priority_filter:
value: "priority = 'high' AND status = 'open'"
summary: Filter to high priority open items
description: |-
Optional WHERE clause to filter a subset of the shape data.
Presence of this or other `subset__*` parameters in the request makes the server
return a subset snapshot instead of the regular shape sync.
This allows you to fetch a specific portion of the shape's data with
additional filtering beyond the main shape's WHERE clause. This filter is
always applied in addition to the main shape's WHERE clause, so it's not possible
to get data that doesn't match the main shape's WHERE clause.
- name: subset__params
in: query
schema:
type: string
format: json
description: |-
Parameters for the subset WHERE clause as a JSON string.
The JSON should be an object mapping positional parameter numbers to their values,
for example: `{"1":"value1","2":"value2"}` to replace `$1` and `$2` in the subset WHERE clause.
Presence of this or other `subset__*` parameters in the request makes the server
return a subset snapshot instead of the regular shape sync.
examples:
single_param:
value: '{"1":"high"}'
summary: Replace $1 in subset WHERE clause with "high"
multiple_params:
value: '{"1":"active","2":"100"}'
summary: Replace $1 with "active" and $2 with "100"
- name: subset__limit
in: query
schema:
type: integer
minimum: 1
examples:
page_size:
value: 50
summary: Return maximum of 50 rows
small_batch:
value: 10
summary: Small batch of 10 rows
description: |-
Maximum number of rows to return in the subset snapshot.
Presence of this or other `subset__*` parameters in the request makes the server
return a subset snapshot instead of the regular shape sync.
When `limit` or `offset` is specified, `subset__order_by` becomes required.
- name: subset__offset
in: query
schema:
type: integer
minimum: 0
examples:
first_page:
value: 0
summary: Start from the first row
second_page:
value: 50
summary: Skip first 50 rows for pagination
description: |-
Number of rows to skip in the subset snapshot (for pagination).
Presence of this or other `subset__*` parameters in the request makes the server
return a subset snapshot instead of the regular shape sync.
When `limit` or `offset` is specified, `subset__order_by` becomes required.
- name: subset__order_by
in: query
schema:
type: string
examples:
by_created:
value: 'created_at DESC'
summary: Order by creation date, newest first
by_priority:
value: 'priority ASC, created_at DESC NULLS LAST'
summary: Order by priority ascending, then by date descending
description: |-
ORDER BY clause for the subset snapshot, determining the row ordering. Uses
same syntax as `ORDER BY` clause in PostgreSQL.
Presence of this or other `subset__*` parameters in the request makes the server
return a subset snapshot instead of the regular shape sync.
This becomes required when using `subset__limit` or `subset__offset`.
- $ref: '#/components/parameters/secret'
- $ref: '#/components/parameters/api_secret'
# Headers
- name: If-None-Match
in: header
schema:
type: string
# TODO: is this description below correct?
description: Re-validate the shape if the etag doesn't match.
responses:
'200':
description: The shape request was successful.
headers:
cache-control:
schema:
type: string
example: 'public, max-age=60, stale-while-revalidate=300'
description: |-
Cache control header as a string of comma separated directives.
Supported directives are: `max-age`, `stale-while-revalidate`.
etag:
schema:
type: string
example: '3833821-1721812114261:26800584_4:26800584_4'
description: |-
Etag header specifying the shape handle and offset for efficient caching.
In the format `{shape_handle}:{start_offset}:{end_offset}`.
electric-cursor:
schema:
type: string
example: '1674440'
description: |-
If present, provides a cursor to use as the value of the `cursor`
parameter in the next `live` mode long polling request.
This works around some inconsistent request coalescing behaviour
with different CDNs.
electric-offset:
schema:
type: string
example: '26800584_4'
description: |-
The latest offset in the batch of data you have received.
If no data is returned, this will be equal to the `offset` parameter
you have provided.
Must be used as the value of the `offset` parameter in your
next request.
electric-handle:
schema:
type: string
example: '3833821-1721812114261'
description: |-
The shape handle.
Must be provided as the value of the `handle` parameter when making
subsequent requests where `offset` is not `-1`.
electric-schema:
schema:
type: string
example: '{"id":{"type":"int4","dimensions":0},"title":{"type":"text","dimensions":0},"status":{"type":"text","dimensions":0,"max_length":8}}'
description: |-
Only present on responses to non-live requests.
A JSON string of an object that maps column names to the corresponding schema object.
The schema object contains the type of the column, the number of dimensions, and possibly additional properties.
Non-array types have a dimension of `0`, while array types have a dimension of 1 or more.
For instance, an array of booleans would have a type of `bool` and a dimension of `1`.
Some types provide additional properties,
e.g.: `VARCHAR(8)` has an additional `“max_length": 8` property,
`BPCHAR(9)` has an additional `"length": 9` property,
`TIME(3)` has an additional `"precision": 3` property,
`NUMERIC(8,5)` has additional `"precision": 8` and `"scale": 5` properties,
`INTERVAL(4)` has an additional `"precision": 4` property,
`INTERVAL MINUTE TO SECOND` has an additional `"fields": "MINUTE TO SECOND"` property,
`BIT(5)` has an additional `"length": 5` property.
electric-up-to-date:
schema:
description: |-
If present, this header indicates that the response ends with
an `up-to-date` control message, indicating that the client has
received all of the data that the server is aware of and can
safely process/apply any accumulated messages.
content:
application/json:
schema:
oneOf:
- type: array
description: |-
Array of message objects returned for regular shape sync requests
(when no `subset__*` parameters are present).
items:
type: object
description: Message object
required:
- headers
properties:
headers:
type: object
description: |-
Metadata about the message.
Messages can be `control` messages, providing information or
instructions to the client. Or they can be operations that
performed a certain `operation` on a row of data in the shape.
Control messages include:
- `up-to-date`: Indicates the client has received all available data
- `must-refetch`: Indicates the client must discard local data and re-sync
- `snapshot-end`: Marks the end of a subset snapshot, includes PostgreSQL
snapshot metadata (xmin, xmax, xip_list) for tracking which changes to skip
properties:
control:
type: string
enum:
- up-to-date
- must-refetch
- snapshot-end
xmin:
type: string
description: |-
Minimum transaction ID in the snapshot (for `snapshot-end` control messages only).
Part of the PostgreSQL snapshot metadata that allows clients to determine
which changes have been incorporated into a snapshot.
xmax:
type: string
description: |-
Maximum transaction ID in the snapshot (for `snapshot-end` control messages only).
Part of the PostgreSQL snapshot metadata that allows clients to determine
which changes have been incorporated into a snapshot.
xip_list:
type: array
items:
type: string
description: |-
List of transaction IDs in progress during the snapshot (for `snapshot-end` control messages only).
Part of the PostgreSQL snapshot metadata that allows clients to determine
which changes have been incorporated into a snapshot.
operation:
type: string
enum:
- insert
- update
- delete
description: The type of operation performed on the row of the shape.
lsn:
type: string
description: |-
The logical sequence number of the operation.
Only present on operations that were received from the event stream.
It's missing on initial query results and on compacted items.
Operations with the same LSN were committed in the same transaction and
can be ordered by `op_position` within the same LSN.
op_position:
type: integer
description: |-
The position of the operation in the transaction.
Only present on operations that were received from the event stream.
It's missing on initial query results and on compacted items.
last:
type: boolean
description: |-
Whether this is the last operation in the transaction for this shape.
Last operation in a transaction for the shape does not mean a last
operation in the transaction for the database.
Only present on operations that were received from the event stream.
It's missing on initial query results and on compacted items.
txids:
type: array
description: |-
The list of transaction IDs that this operation was part of.
Currently, this will only contain a single transaction ID, but future
stream processors may merge operations from multiple transactions into a single
operation in the event stream.
snapshot_mark:
type: integer
description: |-
Random number identifying which snapshot this operation belongs to.
Only present on operation messages that are part of a subset snapshot response.
Used to match snapshot data with its corresponding `snapshot-end` control message.
key:
type: string
description: Row ID
value:
type: object
description: |-
The row data.
Note that this does not necessarily contain the whole row:
- for inserts it will contain the whole row
- for updates it will contain the primary key and the changed values
- for deletes it will contain just the primary key
The values are strings that are formatted according to Postgres' display settings.
Some Postgres types support several display settings, we format values consistently according to the following display settings:
- `bytea_output = 'hex'`
- `DateStyle = 'ISO, DMY'`
- `TimeZone = 'UTC'`
- `IntervalStyle = 'iso_8601'`
- `extra_float_digits = 1`
old_value:
type: object
description: |-
The previous value for changed columns on an update.
Only present on update messages when `replica=full`.
- type: object
description: |-
Object containing subset snapshot data and metadata, returned when
any `subset__*` parameters are present in the request.
required:
- data
- metadata
properties:
metadata:
type: object
description: |-
PostgreSQL snapshot metadata for tracking which changes to skip.
This response format is returned when any `subset__*` parameters are present in the request.
properties:
xmin:
type: string
description: Minimum transaction ID in the snapshot (uint64 as string).
xmax:
type: string
description: Maximum transaction ID in the snapshot (uint64 as string).
xip_list:
type: array
items:
type: string
description: List of transaction IDs that were in progress during the snapshot (uint64 as strings).
snapshot_mark:
type: integer
description: Random number identifying this snapshot, matching the value in operation headers.
database_lsn:
type: string
description: Database log sequence number at the time the snapshot was taken.
data:
type: array
description: Array of operation messages (no control messages) representing the subset snapshot data.
items:
type: object
description: Operation message
properties:
headers:
type: object
description: Metadata about the operation.
properties:
operation:
type: string
enum:
- insert
- update
- delete
description: The type of operation performed on the row of the shape.
snapshot_mark:
type: integer
description: |-
Random number identifying this snapshot.
Matches the `snapshot_mark` in the metadata object.
lsn:
type: string
description: |-
The logical sequence number of the operation.
Only present on operations that were received from the event stream.
It's missing on initial query results and on compacted items.
op_position:
type: integer
description: |-
The position of the operation in the transaction.
Only present on operations that were received from the event stream.
txids:
type: array
description: |-
The list of transaction IDs that this operation was part of.
key:
type: string
description: Row ID
value:
type: object
description: The row data.
old_value:
type: object
description: |-
The previous value for changed columns on an update.
Only present when `replica=full`.
examples:
default:
summary: Regular shape sync with insert operations
value:
# --- truncated at 32 KB (47 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/electric-sql/refs/heads/main/openapi/electric-sql-http-sync-api-openapi.yml