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

# Quickstart

> Write three blocks, then read them as of two dates and see the same data answer differently. That is where tckg parts ways with other memory stores.

## Before you start

* The tckg API address. The hosted one is `https://tckg.factagora.com`; or [run it locally](/index#run-it-locally) at `localhost:8765`.
* `curl` and something to read JSON. There is no SDK yet; every example below pastes as is.
* On the hosted deployment, add `-H "Authorization: Bearer $TCKG_TOKEN"` to every request below. Randy hands out one API key per app; the key decides which tenant your rows land in. A local server needs no key.

```bash theme={null}
export TCKG=https://tckg.factagora.com
export TCKG_TOKEN=...   # your app's API key, from Randy via the password manager
```

<Steps>
  <Step title="Write">
    `space` says **whose memory this is**: one user, one agent, or one worldview. Reads and searches are scoped by space.

    Each node and edge carries **when it was said** (`asserted_at`) and **from when it holds** (`valid_from`).
    **You do not send when it was stored.** The server stamps that. Sending it gets the row refused.

    <CodeGroup>
      ```bash Request theme={null}
      curl -s -X POST $TCKG/v1/memories -H 'content-type: application/json' -d '{
        "space": "user:randy",
        "nodes": [
          {"id": "nvda", "kind": "entity", "statement": "NVDA",
           "asserted_at": "2024-01-15T00:00:00Z", "valid_from": "2024-01-15T00:00:00Z"},
          {"id": "c1", "kind": "claim", "statement": "NVDA goes up this year",
           "asserted_at": "2024-01-15T00:00:00Z", "valid_from": "2024-01-15T00:00:00Z",
           "payload": {"about": {"start": "2024-01", "end": "2024-12"}}}
        ],
        "edges": [
          {"source_id": "c1", "target_id": "nvda", "edge_type": "MENTIONS",
           "asserted_at": "2024-01-15T00:00:00Z", "valid_from": "2024-01-15T00:00:00Z"}
        ]
      }'
      ```

      ```json Response theme={null}
      {"accepted": 3, "refused": [], "warned": [],
       "captured_at": "2026-10-03T07:12:41.183412+00:00", "backfill_batch": null}
      ```
    </CodeGroup>

    `captured_at` is **the instant the server learned this**. Keep it; the next step uses it.

    Send the same request again and you get `accepted: 0` with three `already_remembered` warnings. Retries are safe.
  </Step>

  <Step title="Read, as of two dates">
    Every read **must** carry `as_of`. Without it you get a 400. A default of "now" would let a backtest quietly see the future.

    **As of June 2024** there is nothing. We did not know this yet.

    <CodeGroup>
      ```bash Request theme={null}
      curl -s "$TCKG/v1/memories?space=user:randy&as_of=2024-06-01"
      ```

      ```json Response theme={null}
      {"items": [], "masked": {"node": 2, "edge": 1},
       "certificate": {"as_of": "2024-06-01T23:59:59.999999+00:00",
                       "tx_as_of": "2026-10-03T07:13:02.51+00:00",
                       "masked": {"node": 2, "edge": 1}}}
      ```
    </CodeGroup>

    `masked` is the point. **The answer is not "nothing". The answer is "two things, hidden".**

    **As of the instant it was stored**, both are there.

    <CodeGroup>
      ```bash Request theme={null}
      curl -s -G "$TCKG/v1/memories" --data-urlencode "space=user:randy" \
        --data-urlencode "as_of=2026-10-03T07:12:41.183412+00:00"
      ```

      ```json Response theme={null}
      {"items": [
         {"id": "c1", "kind": "claim", "tenant": "public", "space": "user:randy",
          "statement": "NVDA goes up this year", "category": null,
          "payload": {"about": {"start": "2024-01", "end": "2024-12"}},
          "asserted_at": "2024-01-15T00:00:00+00:00",
          "captured_at": "2026-10-03T07:12:41.183412+00:00",
          "valid": {"from": "2024-01-15T00:00:00+00:00", "to": null}},
         {"id": "nvda", "kind": "entity", "...": "..."}],
       "masked": null, "certificate": {"...": "..."}}
      ```
    </CodeGroup>

    Reading one block that was not there yet tells you **which kind of nothing** it is. `not_yet` means known now but not then. `absent` means never heard of.

    ```bash theme={null}
    curl -s "$TCKG/v1/memories/c1?as_of=2024-06-01"
    # {"item": null, "reason": "not_yet", "certificate": {...}}
    ```
  </Step>

  <Step title="Put old knowledge at its old time (backfill)">
    When you move data you already had, you can **declare** "we actually knew this since April 2024".
    The declaration is never hidden: every certificate over this data carries a `backfill` entry.

    ```bash theme={null}
    curl -s -X POST $TCKG/v1/memories -H 'content-type: application/json' -d '{
      "space": "user:randy",
      "backfill": {"declared_captured_at": "2024-04-15T00:00:00Z", "reason": "import from notes"},
      "nodes": [
        {"id": "c2", "kind": "claim", "statement": "Bond yields rise after the rate hike",
         "asserted_at": "2024-04-10T00:00:00Z", "valid_from": "2024-04-10T00:00:00Z"}
      ],
      "edges": [
        {"source_id": "c2", "target_id": "c1", "edge_type": "CONTRIBUTING_FACTOR",
         "asserted_at": "2024-04-10T00:00:00Z", "valid_from": "2024-04-10T00:00:00Z"}
      ]
    }'
    ```

    Read as of `2024-06-01` again: `c2` is visible, `c1` is still masked. When the declared instant and the server's instant differ, the declaration wins for masking. Both are kept.
  </Step>

  <Step title="Search">
    Candidates come from full-text search and, if you pass one, cosine over your embedding. They are then expanded along causal and temporal edges up to `depth`.
    **Candidates and every hop** use only what was known at `as_of`.

    <CodeGroup>
      ```bash Request theme={null}
      curl -s -X POST $TCKG/v1/search -H 'content-type: application/json' -d '{
        "query": "interest rates", "space": "user:randy", "as_of": "2024-06-01", "depth": 2
      }'
      ```

      ```json Response theme={null}
      {"hits": [
         {"node": {"id": "c2", "statement": "Bond yields rise after the rate hike", "...": "..."},
          "score": 0.0163, "depth": 0, "path": ["c2"], "via": null, "superseded_by": null}],
       "masked": {"node": 2, "edge": 1}, "certificate": {"...": "..."}}
      ```
    </CodeGroup>

    When a later block reverses `c1` through a `SUPERSEDES` edge, `c1` **does not disappear**. It comes back with `superseded_by` set to the successor's id.
    To use embeddings, pass the query vector in `embedding`. It must come from the same model as the `embedding` you stored on the nodes.
  </Step>

  <Step title="Why">
    The evidence chain of one block: causes, effects, support and contradiction, what it replaced and what replaced it, **in the order they were said**.

    <CodeGroup>
      ```bash Request theme={null}
      curl -s "$TCKG/v1/memories/c1/why?as_of=2026-10-03T08:00:00Z"
      ```

      ```json Response theme={null}
      {"chain": [
         {"node": {"id": "c2", "...": "..."}, "depth": 1, "path": ["c1", "c2"],
          "via": "CONTRIBUTING_FACTOR", "role": "cause"},
         {"node": {"id": "c1", "...": "..."}, "depth": 0, "path": ["c1"], "via": null, "role": "subject"}],
       "certificate": {"...": "..."}}
      ```
    </CodeGroup>

    Ask with `as_of` in June 2024 and `c1` is not there: `chain: [], reason: "not_yet"`.
  </Step>
</Steps>

## Next

<CardGroup cols={3}>
  <Card title="Concepts" icon="clock" href="/concepts">What `as_of`, `valid_at`, space, and the certificate mean exactly.</Card>
  <Card title="Resolve" icon="scale-balanced" href="/api-reference/resolve">One value for one declared fact, chosen by a policy you declared.</Card>
  <Card title="Migrate from Supabase" icon="database" href="/migrate-from-supabase">If your graph already lives in your own tables.</Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.