Skip to main content
Every completed run returns an edit_url alongside the download_url. Drop it in an <iframe> and your end-users see the generated document rendered as a full spreadsheet / document / presentation surface inside your app — they can scroll, zoom, switch sheets or slides, and inspect everything the agent produced.
{
  "run_id": "run_01HXYZ",
  "download_url": "https://backend.overtenai.com/api/v1/runs/run_01HXYZ/download",
  "edit_url":     "https://backend.overtenai.com/api/v1/edit/run_01HXYZ?token=...",
  ...
}
It’s called edit_url because the surface behind it is the Overten editor — the same editor the Overten web app uses for its authenticated users. End-users can browse and edit the document live; changes (cell values, text, slide content) persist back to the run’s artifact, so the next download_url fetch serves the edited version. For programmatic / agent-driven edits, call the relevant generate endpoint again with the same run_id and a new prompt — it composes with manual edits.

download_url vs edit_url — when to use which

download_urledit_url
What you getA URL to the raw file bytesAn iframe-ready URL to a live document viewer
Primary useSave to disk, email, feed another pipelineShow the AI’s output inside your app so the user can inspect it
Interactive?No — one-way pullYes — scroll, zoom, switch sheets/slides, click cells
User can edit?N/A (it’s a file)Yes — edits persist to the run’s artifact
Lifetime24 hours (signed storage URL)15 minutes, with sliding refresh on activity
Cross-app safe to share?No (grants file access)No (grants view access to one run)
Most integrations use both: edit_url inside their dashboard so users see results immediately, download_url on a “Download” button so users can take the file elsewhere.

How it works end-to-end

  Your app                 Overten API                 Overten storage
  ────────                 ───────────                 ───────────────

     │ 1. Submit prompt
     │────────────────────────>│
     │                         │   run agent, persist file
     │                         │─────────────────────────────>│
     │<──── download_url       │
     │      edit_url(token) ───│

     │ 2. <iframe src={edit_url}>
     │─────────────────────────┐
     │                         │  validate token
     │                         │  load file from storage
     │                         │<────────────────────────────│
     │   3. Rendered document  │
     │<────────────────────────│
  1. You call POST /excel/generate (or word/slides), get back edit_url.
  2. You embed that URL in an iframe in your UI.
  3. Overten validates the token, loads the run’s file from storage, and streams a rendering of the document into the iframe.
  4. Every subsequent user action (scroll, click, zoom) stays between the browser and Overten — your app never sees the content, never handles bytes, never runs a viewer itself.
The token is what ties “this iframe open right now” to “this specific run”. It expires and is scoped — see below.

Token semantics

  • Opaque — a random string, not a JWT. Generated by Overten, validated by Overten. Don’t try to parse it client-side.
  • Scoped to one run + your org — if someone copy-pastes the URL into another app, the token still only unlocks this document for your org.
  • Short-lived, sliding — 15 minutes from the last user activity. Active viewer sessions refresh the TTL automatically; idle sessions (user closed the tab) expire on their own.
  • No explicit revoke endpoint today. The short sliding TTL is the mitigation. If you suspect a leak before a token has expired naturally, contact support with the run_id and we can invalidate on our side.

Basic embed

<iframe
  src="{{ run.edit_url }}"
  width="100%"
  height="800"
  allow="clipboard-read; clipboard-write"
  sandbox="allow-scripts allow-same-origin allow-forms"
></iframe>
React:
function DocumentViewer({ run }) {
  return (
    <iframe
      src={run.edit_url}
      title="Document viewer"
      style={{ width: "100%", height: "100vh", border: 0 }}
      allow="clipboard-read; clipboard-write"
    />
  );
}

How edits land

Two paths — they compose:
  1. Direct in-iframe edits. The user types, changes a cell, rewrites a paragraph, moves a slide. Edits auto-save to the run’s artifact (same storage location download_url points at). The next fetch serves the updated bytes.
  2. Agent-driven edits via API. Call the relevant generate endpoint with the original run_id and a new prompt; the agent picks up whatever state is current (including the user’s in-iframe edits) and applies the change:
    resp = requests.post(
        f"{API}/excel/generate",
        headers=headers,
        json={
            "run_id": "run_01HXYZ",
            "prompt": "Apply a % YoY column to the Summary sheet "
                      "and extend the headcount chart through Q4.",
        },
    )
    
Agent follow-ups via run_id are cheap because the agent resumes from state — it’s not rebuilding the workbook, just applying the delta. Every generate response includes fresh edit_url and download_url.

Getting a fresh URL after expiry

If the original edit_url has expired (e.g. the user opens your dashboard the next morning and tries to reopen a doc from yesterday), call the generate endpoint again with the original run_id and a no-op prompt:
curl -X POST "https://backend.overtenai.com/api/v1/excel/generate" \
  -H "X-API-Key: $OVERTEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "run_id": "run_01HXYZ",
    "prompt": "(no change — just reopening the session)"
  }'
The response includes a fresh edit_url with a new token. Resume via run_id is cheap because the agent already has state.

What edit_url is NOT

  • Not a shareable public link. Tokens are scoped to one viewer session. Don’t email an edit_url — it’ll work for 15 minutes, then stop, and before then it unlocks your org’s run to whoever clicked.
  • Not a download. The iframe renders the document live; bytes never land on the user’s disk via this route. For downloads, use download_url.
  • Not a programmatic write API. In-iframe edits work for human users, but your server-side code should use the generate endpoint with run_id to apply structured changes — that’s auditable, has a ledger row, and doesn’t require a browser.
  • Not a hosted Office install. Your end-user doesn’t need Excel / Word / PowerPoint locally. The viewer runs entirely in the browser.

Security notes

  • Mint per viewer. Don’t store one edit_url server-side and serve it to multiple users. The token is meant for one session — each user should get a freshly-minted URL.
  • Strip tokens from logs / analytics. Drop the token query param before recording page URLs in your observability tools.
  • Restrict frame ancestors. If you serve edit_url in an <iframe>, set a Content-Security-Policy on your parent page that limits which origins can frame your dashboard, so an attacker can’t frame your app and read the iframe’s state via JS.
  • Prefer your own domain for the parent page. The token is only meaningful in-browser; someone who captures the URL during a session and opens it from another host still has to get past the sliding TTL.

White-label / custom branding

By default, the viewer shows standard Overten chrome (header, logo). Enterprise customers can get a white-label version with custom branding — contact support. If you don’t want to use the Overten viewer at all:
  • Use download_url and render the file with your own tooling (Google Drive viewer, OnlyOffice, SheetJS, native Office apps).
  • Or fetch the file bytes server-side and hand them to your preferred renderer.
The API returns both download_url and edit_url on every run, so you can switch between approaches per-user or per-route without changing your generation code.