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

# Quickstart

> Get setup with Chronicle API fast

<Info>
  Before you begin, you'll need:

  * Chronicle workspace with API enabled (available on Pro, Plus, and Max plans)
  * Node.js 18+ or another runtime with `fetch` support
</Info>

<Steps>
  <Step title="Create an API key" iconType="solid" titleSize="h3">
    * Open [Chronicle](https://app.chroniclehq.com)
    * Navigate to Settings → API
    * Create an API key
    * Copy and store it securely, you won't be able to see it again

    ```shellscript theme={null}
    export CHRONICLE_API_KEY="your_api_key_here"
    ```

    Include the key in the header with every request. Supported headers:

    * `Authorization: Bearer <API_KEY>`
    * `x-api-key: <API_KEY>`
  </Step>

  <Step title="Upload reference files (optional)" iconType="solid" titleSize="h3">
    Skip this step if your prompt doesn't need source files. Otherwise, request a presigned upload target, post the file directly to S3, and keep the returned `download_url` for the next step.

    <CodeGroup>
      ```shellscript cURL theme={null}
      # 1. Request a presigned target
      TARGET=$(curl -s -X POST "https://api.chroniclehq.com/api/v1/uploads/create-target" \
        -H "Authorization: Bearer $CHRONICLE_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "file_name": "research-notes.pdf",
          "content_type": "application/pdf",
          "declared_file_size": 524288
        }')

      URL=$(echo "$TARGET" | jq -r '.url')
      DOWNLOAD_URL=$(echo "$TARGET" | jq -r '.download_url')
      FIELDS_ARGS=$(echo "$TARGET" | jq -r '.fields[] | "-F \(.name)=\(.value)"')

      # 2. POST the file to S3 (no Authorization header, file last)
      eval "curl -X POST \"$URL\" $FIELDS_ARGS -F file=@research-notes.pdf"
      ```

      ```javascript JavaScript theme={null}
      async function uploadAttachment(file) {
        const target = await chronicleFetch("/uploads/create-target", {
          method: "POST",
          body: JSON.stringify({
            file_name: file.name,
            content_type: file.type,
            declared_file_size: file.size,
          }),
        });

        const form = new FormData();
        for (const f of target.fields) form.append(f.name, f.value);
        form.append("file", file); // must be last

        const res = await fetch(target.url, { method: "POST", body: form });
        if (!res.ok) throw new Error(`S3 upload failed: ${res.status}`);

        return {
          id: crypto.randomUUID(),
          url: target.download_url,
          file_name: file.name,
          type: file.type,
        };
      }
      ```
    </CodeGroup>

    Allowed formats: `.txt`, `.md`, `.pdf`, `.pptx`, and common image types. Max 50 MB per file. See [POST /uploads/create-target](/post-uploads-create-target) for the full reference.
  </Step>

  <Step title="Start a generation from a prompt" iconType="solid" titleSize="h3">
    If you uploaded files in the previous step, pass them in the `attachments` array using the `download_url` you captured.

    <CodeGroup>
      ```shellscript cURL theme={null}
      curl -X POST "https://api.chroniclehq.com/api/v1/presentations/generate" \
        -H "Authorization: Bearer $CHRONICLE_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "template_id": "tpl_123",
          "prompt": "Create a 5-slide presentation summarizing our Q2 product launch, customer traction, and next-quarter priorities.",
          "attachments": [
            {
              "id": "att_123",
              "url": "'"$DOWNLOAD_URL"'",
              "file_name": "research-notes.pdf",
              "type": "application/pdf"
            }
          ]
        }'
      ```

      ```javascript JavaScript theme={null}
      async function generatePresentation(templateId, prompt, attachments = []) {
        const data = await chronicleFetch("/presentations/generate", {
          method: "POST",
          body: JSON.stringify({
            template_id: templateId,
            prompt,
            ...(attachments.length > 0 ? { attachments } : {}),
          })
        });

        return data;
      }

      const generationJob = await generatePresentation(
        "tpl_123",
        "Create a 5-slide presentation summarizing our Q2 product launch, customer traction, and next-quarter priorities."
      );

      console.log(generationJob);
      ```
    </CodeGroup>

    The request returns `202 Accepted` with a `generation_id` you can poll.

    <CodeGroup>
      ```json Response theme={null}
      {
        "generation_id": "gen_123",
        "status": "generating",
        "poll_url": "/api/v1/presentations/generate/gen_123/status"
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Poll for completion" iconType="solid" titleSize="h3">
    Poll the status endpoint until `status` is `completed`, `failed`, or `awaiting_input`.

    <CodeGroup>
      ```shellscript cURL theme={null}
      curl -X GET "https://api.chroniclehq.com/api/v1/presentations/generate/gen_123/status" \
        -H "Authorization: Bearer $CHRONICLE_API_KEY"
      ```

      ```javascript JavaScript theme={null}
      async function getGenerationStatus(generationId) {
        const data = await chronicleFetch(
          `/presentations/generate/${generationId}/status`,
          { method: "GET" }
        );

        return data;
      }

      const status = await getGenerationStatus("gen_123");
      console.log(status);
      ```
    </CodeGroup>

    <CodeGroup>
      ```json Completed response theme={null}
      {
        "generation_id": "gen_123",
        "status": "completed",
        "presentation": {
          "id": "pres_789",
          "title": "Q2 Product Launch Review",
          "workspace_id": "ws_123",
          "sections": [],
          "is_public": false,
          "url": "https://app.chroniclehq.com/ws_123/document/pres_789",
          "created_at": "2026-04-30T00:00:00.000Z",
          "updated_at": "2026-04-30T00:00:00.000Z"
        }
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="View and use your Chronicle deck" iconType="solid" titleSize="h3">
    Open the `url` from the completed payload, or fetch the presentation later by ID:

    <CodeGroup>
      ```shellscript cURL theme={null}
      curl "https://api.chroniclehq.com/api/v1/presentations/pres_789" \
        -H "Authorization: Bearer $CHRONICLE_API_KEY"
      ```

      ```javascript JavaScript theme={null}
      const res = await fetch(
        "https://api.chroniclehq.com/api/v1/presentations/pres_789",
        {
          headers: {
            Authorization: `Bearer ${CHRONICLE_API_KEY}`,
          },
        }
      );

      console.log(await res.json());
      ```
    </CodeGroup>

    <CodeGroup>
      ```json Response theme={null}
      {
        "id": "pres_789",
        "title": "Q2 Product Launch Review",
        "workspace_id": "ws_123",
        "sections": [],
        "is_public": false,
        "url": "https://app.chroniclehq.com/ws_123/document/pres_789",
        "created_at": "2026-04-30T00:00:00.000Z",
        "updated_at": "2026-04-30T00:00:00.000Z"
      }
      ```
    </CodeGroup>
  </Step>
</Steps>

***

## Related posts

* [API reference](/api-reference)
* [Authentication](/authentication)
* [Error codes](/error-codes)
