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

# Embed Qirtaas for document creation

> Build a notes feature — create, list, edit, and delete rich documents — with a single change to your backend.

In this tutorial you add a **notes** feature to an existing app: signed-in
users write rich documents (Quran, hadith, matn, full Arabic RTL), see them in
a list, reopen them, and delete them.

The key architectural idea: **Qirtaas is the notes backend**. Documents are
created, listed, and deleted from the frontend over the SDK's embed-token
channel, and each user's embed identity scopes them to their own documents.
Your backend needs exactly **one change** — a token endpoint — and zero new
tables.

**What you'll build**

* Backend: one token endpoint (the only backend change).
* Frontend: a compose page with `<QirtaasEditor>`, a note list driven by
  `listDocuments()`, reopen-to-edit, and delete.

Frontend samples are React; the token endpoint is tabbed Node (Express) and
Python (FastAPI). Prerequisites: a `qrt_sk_…` API key (see the
[Quickstart](/get-started/quickstart#1-get-an-api-key)) and
`npm install @qirtaas/react`.

## 1. Backend — add the token endpoint

The editor authenticates every request with a short-lived embed token, and
only your backend — holding the secret key — can mint one. Gate the endpoint
with your app's own auth and pass the authenticated user's id as
`external_user_id`: Qirtaas auto-provisions an identity per distinct id, and
that identity is what scopes each user to their own documents (creation,
listing, and deletion all happen within it).

<CodeGroup>
  ```ts Node (Express) theme={null}
  // .env: QIRTAAS_API_KEY=qrt_sk_…
  app.post("/api/qirtaas-token", requireAuth, async (req, res) => {
    const upstream = await fetch("https://api.qirtaas.io/v1/embed/tokens/", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.QIRTAAS_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ external_user_id: String(req.user.id) }),
    });
    res.status(upstream.status).json(await upstream.json());
  });
  ```

  ```python Python (FastAPI) theme={null}
  # env: QIRTAAS_API_KEY=qrt_sk_…
  import os
  import httpx
  from fastapi import Depends

  QIRTAAS_API_KEY = os.environ["QIRTAAS_API_KEY"]

  @app.post("/api/qirtaas-token")
  async def qirtaas_token(user=Depends(get_current_user)):
      async with httpx.AsyncClient() as client:
          resp = await client.post(
              "https://api.qirtaas.io/v1/embed/tokens/",
              headers={"Authorization": f"Bearer {QIRTAAS_API_KEY}"},
              json={"external_user_id": str(user.id)},
              timeout=10,
          )
      resp.raise_for_status()
      return resp.json()  # {"token": "…", "expires_at": "…"}
  ```
</CodeGroup>

<Warning>
  Whoever can call this endpoint can edit that identity's documents — always
  gate it with your own authentication and derive `external_user_id` from the
  session, never from the request body.
</Warning>

That's the entire backend. Everything below is frontend.

## 2. Frontend — one client, one token fetcher

Create the client once and share it across the feature — the same `getToken`
powers the editor and the list/delete operations:

```ts title="src/qirtaas.ts" theme={null}
import { createQirtaasClient } from "@qirtaas/react";

export async function getToken(): Promise<string> {
  const res = await fetch("/api/qirtaas-token", { method: "POST" });
  if (!res.ok) throw new Error(`token fetch failed (${res.status})`);
  const { token } = await res.json();
  return token;
}

export const qirtaas = createQirtaasClient({ getToken });
```

The SDK calls `getToken` on init, proactively before the token's 1-hour
expiry, and once more after a `401` — so it should simply mint fresh every
call.

## 3. Frontend — the compose page

Mount `<QirtaasEditor>` **without** a `documentId`: the SDK lazily creates the
document on the first keystroke and hands you the new id via
`onDocumentCreated`. There is nothing to persist yourself — the document is
already stored under the user's identity, and autosave keeps it current as
they type.

```tsx title="src/ComposeNote.tsx" theme={null}
import { QirtaasEditor } from "@qirtaas/react";
import { getToken } from "./qirtaas";

export function ComposeNote({ onCreated }: { onCreated: (id: string) => void }) {
  return (
    // The editor fills its container — give it a bounded height.
    <div style={{ height: "70vh" }}>
      <QirtaasEditor
        getToken={getToken}
        locale="ar"
        theme="light"
        // e.g. update the route from /notes/new to /notes/<id>
        onDocumentCreated={onCreated}
        onSaveStateChange={(state) => console.log("save:", state)}
      />
    </div>
  );
}
```

Use `onDocumentCreated` for navigation concerns (swap `/notes/new` for
`/notes/<id>` so a refresh reopens the same note) and `onSaveStateChange` for
a saved/saving badge.

## 4. Frontend — list the notes

`listDocuments()` returns the identity's documents with **server-derived
titles** (the first words of the content), so the list is directly
displayable — no table of your own, no title field to maintain:

```tsx title="src/NoteList.tsx" theme={null}
import { useEffect, useState } from "react";
import type { DocumentSummary } from "@qirtaas/react";
import { qirtaas } from "./qirtaas";

export function NoteList({ onOpen }: { onOpen: (id: string) => void }) {
  const [notes, setNotes] = useState<DocumentSummary[]>([]);

  useEffect(() => {
    qirtaas.listDocuments().then(setNotes);
  }, []);

  return (
    <ul>
      {notes.map((note) => (
        <li key={note.id} onClick={() => onOpen(note.id)}>
          {note.title || "Untitled"}
          <time>{new Date(note.updated_at).toLocaleDateString()}</time>
        </li>
      ))}
    </ul>
  );
}
```

Render the list with your own markup — it's plain data. Mount a Qirtaas embed
only on the compose/read page, not per list item.

## 5. Frontend — reopen and keep editing

To continue an existing note, pass its id as `documentId`. The component
mounts the editor **once**, so when the user switches notes, force a remount
with a `key`:

```tsx title="src/EditNote.tsx" theme={null}
import { QirtaasEditor } from "@qirtaas/react";
import { getToken } from "./qirtaas";

export function EditNote({ docId }: { docId: string }) {
  return (
    <div style={{ height: "70vh" }}>
      <QirtaasEditor
        key={docId}
        documentId={docId}
        getToken={getToken}
        locale="ar"
        theme="light"
      />
    </div>
  );
}
```

Because the embed token is minted for the same `external_user_id`, the owner
can reopen and edit any of their documents — no extra grant needed.

## 6. Frontend — delete a note

```tsx theme={null}
import { qirtaas } from "./qirtaas";

async function deleteNote(id: string) {
  await qirtaas.deleteDocument(id);
  setNotes(await qirtaas.listDocuments()); // refresh the list
}
```

The whole loop — create, list, edit, delete — never touched your backend
again after step 1.

## When you outgrow this

The zero-model setup carries you as far as "each user manages their own
documents". Add a table of your own the moment a document needs **your**
data attached to it:

* **Host metadata** — covers, tags, summaries, or a curated ordering that
  Qirtaas doesn't store. Keep a row per note holding the `qirtaas_doc_id`
  pointer plus your fields; the content itself stays in Qirtaas.
* **Cross-user access** — letting *other* users read a document requires your
  backend to know which document belongs to what, so it can decide whom to
  authorize. That's the pointer-table pattern, covered in
  [Access control](/tutorials/access-control).

## Where you are

Users can author, list, reopen, and delete rich documents, and your backend
grew by one endpoint. Next:

<CardGroup cols={2}>
  <Card title="Document reading" icon="book-open" href="/tutorials/document-reading">
    Show these notes read-only with the renderer.
  </Card>

  <Card title="Access control" icon="lock" href="/tutorials/access-control">
    Let other users read them, gated by your own ACL.
  </Card>
</CardGroup>
