> ## Documentation Index
> Fetch the complete documentation index at: https://docs.moralis.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart: Run Your First Sink

> Get live, decoded onchain data flowing into your own database in about ten minutes. Generate a sink starter pack, run it with Docker, and query your first rows.

This quickstart takes you from nothing to **live onchain data in your own database** in about ten minutes.
You generate a **sink starter pack** in the Admin Panel, run it with Docker, and query your first rows. Every
[recipe](/data-feeds/recipes/overview) and [migration guide](/data-feeds/migration/overview)
then follows the same pattern.

## Before you begin

<Warning>
  **Data Feeds is a technical product: you run the infrastructure.** Unlike the REST API, the data lands in a
  database **you own and operate**: you run the Docker sink, you provision Postgres/ClickHouse (locally for this
  quickstart; on your own servers in production), and you are responsible for storing, sizing, and backing up
  the decompressed data. Decoded onchain data expands significantly from its compressed transport size, so budget
  disk accordingly.
</Warning>

* **Docker** and Docker Compose installed and running.
* A **Data Feeds API key** (`cm_live_...`). [Request access](https://moralis.com/data-feeds/request-access) if
  you do not have one.
* Access to the **Moralis Admin Panel**, where you generate the sink config.
* A machine with enough **disk headroom** for your backfill window: start small (`tip-500`) and widen once
  you've seen the per-block footprint for your chain.

## Step 1: generate your sink starter pack

In the Admin Panel, configure a sink (pick your **chain**, **topic**, and **destination database**) and
download the **starter pack** (`continuum-sink-starter.zip`). Unzip it. You get:

| File                 | What it is                                                                                   |
| -------------------- | -------------------------------------------------------------------------------------------- |
| `sink-config.yaml`   | The sink config you generated: Continuum URL, `namespace` (chain), `topic`, and destination. |
| `docker-compose.yml` | Brings up the sink (`moralisweb3/continuum-sink`) plus a local PostgreSQL.                   |
| `.env`               | Your API key and DB password. **Do not commit or share it.**                                 |
| `schema.sql`         | DDL for the destination table(s), auto-applied to Postgres on first start.                   |
| `README.txt`         | The same steps you are reading here.                                                         |

<Note>
  **`namespace` is the chain, as a hex chain ID.** For example `0x1` is Ethereum, `0xa4b1` is Arbitrum One. See
  the [chain reference](#chain-reference) below. `topic` (e.g. `master-3`) is the master block stream the sink
  reads.
</Note>

## Step 2: add your API key

Open `.env` and confirm your key is set; the generated file sometimes ships with a **blank or placeholder**
key:

```bash theme={null}
# .env  (do not commit)
MORALIS_API_KEY=cm_live_...      # your full key — the sink fails with "api_key is required" if blank
SINK_PG_PASSWORD=...             # already set for the bundled Postgres
```

<Warning>
  **Keep your key private.** It is a live credential. Never commit `.env` or paste the key into shared docs or
  chat. If a key leaks, rotate it.
</Warning>

## Step 3: start the stack

From the unzipped directory:

```bash theme={null}
docker compose up -d
```

This starts PostgreSQL (with `schema.sql` applied) and the sink. The sink reads a backfill window from the
upstream S3 store, then tails live blocks. Watch it work:

```bash theme={null}
docker compose logs -f sink
```

<Tip>
  **Querying from your own app on the host?** The bundled Postgres is only reachable inside the Docker network
  by default. Add a port mapping to the `postgres` service so you can connect from your machine:

  ```yaml theme={null}
    postgres:
      ports:
        - "5544:5432"
  ```

  Then connect to `localhost:5544` (user/db `sdf`, password from `.env`).
</Tip>

## Step 4: query your first rows

The default `tokenTransfers` config writes to a table named after your sink (here, `sdf`):

```bash theme={null}
docker compose exec postgres psql -U sdf -d sdf -c \
  "SELECT count(*), max(position) AS latest_block FROM public.sdf;"
```

```sql theme={null}
-- the most recent transfers
SELECT position, tokenaddress, fromaddress, toaddress, value
FROM public.sdf
ORDER BY position DESC
LIMIT 20;
```

You should see the count climb as new blocks land. That is live, decoded onchain data in a store you own: no
API quotas, no per-call limits.

## If no rows arrive

The pipe has a few moving parts. Work down this list:

| Symptom                                                      | Cause                                                     | Fix                                                                                                       |
| ------------------------------------------------------------ | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `continuum.api_key is required` in sink logs                 | `MORALIS_API_KEY` blank in `.env`                         | Paste your full `cm_live_...` key, `docker compose up -d --force-recreate sink`.                          |
| Sink logs stop after `s3-proxy reachable` and rows stay at 0 | Upstream S3 backfill stalled (e.g. "Partition not found") | Not your setup. Confirm the chain/topic backfill is healthy with the team, then it resumes automatically. |
| Can connect but app can't reach Postgres                     | No host port mapping                                      | Add the `ports` block from Step 3.                                                                        |

## Chain reference

`namespace` in `sink-config.yaml` is the chain's ID in hex:

| Chain        | Chain ID | `namespace` |
| ------------ | -------- | ----------- |
| Ethereum     | 1        | `0x1`       |
| Optimism     | 10       | `0xa`       |
| BNB Chain    | 56       | `0x38`      |
| Polygon      | 137      | `0x89`      |
| Base         | 8453     | `0x2105`    |
| Arbitrum One | 42161    | `0xa4b1`    |
| Avalanche    | 43114    | `0xa86a`    |

## What just happened

* The **starter pack** carried a generated `sink-config.yaml` (chain, topic, destination) and a matching schema.
* The **sink** (`moralisweb3/continuum-sink`) read the master block stream, backfilled history, then stayed live.
* You queried a normal SQL table, yours to join and shape freely.

## Next steps

* **Migrating an existing integration?** Find your endpoint in the [Migration guides](/data-feeds/migration/overview).
* **Want a different dataset?** Browse the [recipes](/data-feeds/recipes/overview): swaps, balances, holders, prices,
  NFT activity. They all run the same way; swap the transform and schema.
* **Understand the model:** [What are Data Feeds?](/data-feeds/concepts/what-are-data-feeds),
  [History & backfill](/data-feeds/concepts/history-and-backfill), and [Destinations](/data-feeds/concepts/destinations).
* **Hit something surprising?** The [Troubleshooting](/data-feeds/migration/troubleshooting) page covers the common
  first-run questions.
