Skip to content
Helpfeel

Keyline API for Agents: The Runbook

View as Markdown

This page is written to be read by an AI agent as well as a person. It is the whole integration on one page: how to authenticate, the three retrieval calls, what comes back, and the failure modes worth knowing before you hit them.

If you are a person deciding whether to bother, read Keyline for developers instead. If you want to run the calls by hand first, start with the Keyline API quickstart.

Apply for Public Preview access to the app and API at Keyline. This page documents the current source-backed integration contract. It does not prove which revision or access settings are deployed.

What the Keyline API is, for an agent

The Keyline API is a retrieval tool. You call it with a natural-language question. It searches a document library that was indexed earlier, reads the pages worth reading, and returns an answer with the page and the cropped image region each claim came from.

It renders nothing. Text, page numbers, and image crops come back over HTTP for the calling program to display, so the answer appears inside whatever interface you already have: a chat transcript, a ticket view, a sidebar in your own tool. Helpfeel's own rendering of it is the Keyline app.

Reach for it when:

  • The answer is inside a figure, a diagram, or a table, where text extraction loses it.
  • The question spans a library and you do not know which file holds the answer.
  • The user will ask where the answer came from, and a page number alone is not enough.

Do not reach for it when:

  • You already have the text and just need a model to reason over it.
  • You need structured field extraction from one known document. That is a parsing job.
  • The documents are mostly prose. Ordinary text search is fine and cheaper.

Core concepts, in dependency order

TermWhat it means
OrganizationMembership, project, and preview-quota boundary
ProjectThe tenancy unit that owns documents. Every data call is scoped to one project. A project is the corpus
FileOne uploaded document, addressed by a content hash
SnippetOne cropped semantic block of a page: text, figure, table, equation. The atom of a citation
Virtual pageA logical group of snippets that can span a physical page break, so a figure and the paragraph explaining it stay together
BundleSeveral files treated as one target for a deep read
InfoA saved investigation session you can resume later

The two that matter for citations are snippet and virtual page. A snippet is what gets cropped and returned as an image. A virtual page is what the retrieval loop decides to read.

Authentication

One header. Server to server only.

  • Credential: a project-scoped bearer token issued to a service account.
  • Format: the prefix plms_ followed by 43 URL-safe characters.
  • The token carries no permissions of its own. Access is read from the live project grant on every request, so a revoked grant takes effect immediately.
  • The token is shown once, at creation and at reset. Store it as a secret.
  • Regenerating a token invalidates the old one immediately. There is no overlapping validity window, so plan the swap.

Three behaviors to code against:

  1. Bearer never falls back to a session. If a token is malformed, unknown, or revoked, the request fails. It does not degrade into an anonymous request.
  2. Bearer allowlist failures return the same unauthorized response. Malformed, unknown, and revoked tokens are deliberately indistinguishable from requests to paths outside the bearer allowlist. Do not write logic that tries to tell them apart, and do not retry hoping for a different error.
  3. Route permissions are a separate gate. The live project grant is checked after a request passes the bearer allowlist. A route can conceal missing access and a missing resource with the same not-found response. Do not treat 404 as proof that the resource does not exist.

No CORS. Do not call this from a browser. There is no browser SDK and browser usage is out of scope by design.

Configuration your integration needs

Supply all three out of band. A token cannot discover them for you.

ValueWhere it comes from
Base URLProvided with your Public Preview access
Project IDCopied from the project settings page in the Keyline app
TokenCreated once in Project Settings, or provided with access

The project ID is a real onboarding step. A token cannot enumerate the organizations or projects it belongs to, which is a deliberate isolation property.

How a person gets the first project and token

The current source uses Google sign-in. A deployed beta email-domain allowlist and rolling new-user cap can restrict first login. The cap does not block an existing user from signing in.

After an accepted first login, the server creates or repairs a personal organization. If that organization has no project, the client tries to create My Project. A project manager can then create a project-scoped service account in Project Settings. The plaintext token appears only in the create or reset response.

This flow is implementation and test evidence for the pinned source commit. Confirm the deployed revision, sign-in provider, access allowlist, signup cap, and Project Settings flow before you use it as an operational dependency.

The three retrieval calls

Pick by what you know before you ask.

You knowUseShape
Nothing. You have a library and a questionAgentic searchStreamed. A reasoning loop that searches, resolves, reads, and decides when it has enough
Which file or bundle holds the answerDeep readStreamed. A fixed pipeline that reads inside that scope in depth
You only want to know which files mention somethingVector searchOne response, no reasoning, cheapest by a wide margin

The default choice for an agent. One request carries the question, optional prior conversation turns, and the last reference number you used so numbering continues across turns. The project is the corpus and there is no per-call file filter.

Internally it runs a tool loop: search the topic index, resolve promising hits into real content, report progress, and stop when the evidence is sufficient. You do not drive the loop. You consume its output.

Cost scales with library size and you cannot cap it from the request. There is no result-count or depth parameter. On a large library, prefer a project scoped to the documents that matter over one project holding everything.

Deep read

Use when your agent has already identified the file, or when the user named it. Pass the question plus the file, or a bundle plus the specific files within it. Follow-ups pass the root conversation identifier and the server loads the prior turns as context.

Two things to handle:

  • The progress stream analyzes several units in parallel, so per-unit output interleaves. Buffer by unit identifier before displaying anything.
  • Deep read does not save the conversation for you. Inference and persistence are separate calls today. If you want the exchange kept, accumulate the final text and save it in a second request.

One call, one response, no model reasoning. It returns hits grouped by file, where each hit is a topic path through that document's hierarchy with a distance score. Good for "which of these files talk about this" and for narrowing before a deep read.

It does not return page numbers. Resolving a hit to actual page content is not available over a token in this preview, so do not design your own retrieval loop on top of vector search yet. Use agentic search for that and let the server run the loop.

Consuming a streamed answer

Both agentic search and deep read stream. Three kinds of event arrive:

EventWhat to do with it
ProgressSurface it. A long query should not be silent. This is the loop narrating its own steps
Working outputIntermediate analysis, useful for a live view. Not the answer. Buffer by unit identifier because units interleave
Final answerThe synthesized answer, streamed token by token, carrying inline numbered citations
CompletionEnds the stream and carries the structured reference list
ErrorEnds the stream. Surface the failure, do not silently return a partial answer as if it were complete

Disconnecting aborts the work server side, so abandoning a stream does not keep burning cost.

When nothing relevant is found, the answer says so. The pipeline does not fabricate an answer from an empty result. Treat a no-information response as a valid outcome and pass it through honestly rather than re-asking with a looser question.

Citations, and the one rule that matters

Answers carry numbered citations that resolve, through the completion event's reference list, to a file and a unit of content. From there:

  • Page number is available for every citation.
  • The cropped image of the cited block is retrievable as a PNG. This is the highest-value thing the API returns and the reason to prefer it over a text snippet.
  • Region coordinates are available for returned snippet ids through POST .../files/:fileHash/page_image_snippets. The response groups each bounding box by physical page, so the caller can draw the cited region on the full page.

Never fabricate a citation

Only cite identifiers the server actually returned. Do not construct, guess, adjust, or interpolate a citation identifier, and do not carry one over from a previous answer. A citation that does not resolve is worse than no citation, because the whole value of this API is that a claim can be checked.

Rendering guidance that holds up in practice

  • Download images to local paths rather than embedding remote URLs. You cannot control which renderer the user sees, and a local path renders everywhere. Make the download idempotent: if the file is already there, reuse it.
  • Put the crop immediately next to the sentence that cites it. The crop is the argument. Placed at the bottom in a reference list, it is decoration.
  • Show the retrieval loop's progress before the answer lands. Surface which pages are being read as soon as you know, so the user can follow along instead of waiting.
  • Do not expose internal vocabulary to end users. Words like snippet and virtual page are for you. Say "the table on page 212" to the person reading the answer.

Ingesting documents

Upload returns immediately. Everything after it is asynchronous and your integration has to handle that.

  1. Request an upload through POST .../upload/init. A bearer token gets a direct upload target. Current source at commit d6217b923ec8ebcf8b22d9e95e6ee6df45b305fe deliberately excludes the local-development multipart POST .../upload/queue route from bearer access. If upload/init returns { "mode": "multipart" }, stop. That bearer token cannot use the fallback route. The source-defined production configuration uses the direct GCS path. That configuration is evidence of intent, not proof of the deployed revision.
  2. Send the bytes. If you were given a direct target with required headers, send exactly those headers. They are part of the signature and the upload fails without them. The target expires, so do not hold it.
  3. Poll the file's status until it is ready. There are no webhooks. Polling every few seconds is the expected pattern.
  4. Note that a document becomes deep-readable slightly before the project-wide search index finishes. indexing and completed are deep-readable. indexing_failed is also deep-readable, but project-wide vector and agentic search can miss that file until you resume analysis and rebuild the hierarchy and title index.

Practical constraints to plan against:

Accepted formatsPDF, PNG, JPEG, and Office: DOCX, XLSX, PPTX, DOC, XLS, PPT
File size and pagesPreliminary, and adjusted to fit customer needs
ThroughputUploads queue and index in the background
RegionUS region available now

Per-file size and page limits are preliminary and get adjusted to fit customer needs, so do not hard-code a threshold and do not predict how long an import will take. Upload the file, poll its status, and treat a rejected upload as the limit response.

A large first import takes real time. Index a meaningful subset, verify it answers a real question, then widen.

Usage limits are not billing credits

The pinned application source has no credit ledger, dollar-to-credit conversion, paid balance, or Stripe settlement path. It records Gemini token and embedding-character telemetry. It also applies separate trial counters:

  • One accepted upload consumes one file allowance before the direct upload target is issued. Later upload abandonment or processing failure does not restore it. Rejected formats, duplicate queued names, and active-file locks do not consume it.
  • Each accepted deep-read or agentic-search request consumes one question allowance before model work begins. Follow-ups count again. A later model or stream failure does not restore it. Vector search and next-question suggestions do not consume this counter.

These counters are preview limits. They are not the commercial credit schedule. Use the pricing page for the approved offer, and do not infer a charge event or retry refund from these implementation counters.

Bearer permissions and destructive routes

The bearer token reaches an enumerated set of endpoints. Anything outside it is refused even when the account's role would otherwise allow it. This is transport hardening layered on top of normal permissions, and it is expected to relax as rate limiting and audit logging land.

Current source reviewed for this page at commit d6217b923ec8ebcf8b22d9e95e6ee6df45b305fe includes file deletion over a bearer token:

DELETE /api/projects/:projectId/files/:fileHash

The route requires edit project access. A service account with only view access gets a concealed not-found response. A queued or actively processed file returns 409. A successful delete removes the file directory, including saved conversations, and attempts to remove its vector tables and file-list marker. Keep view-only service accounts separate from service accounts that can delete.

The same source places project creation and project service-account list, create, reset, and delete routes on the bearer allowlist. Current handlers and tests confirm that project creation requires organization membership and gives the creator a project manage grant. The four project service-account routes require manage. Create can issue view, edit, or manage, and plaintext tokens appear only in create and reset responses. Project-scoped reset or revoke is refused for a service account shared across projects. This surface lets one token mint another token. Do not give a retrieval-only agent project manage access.

This is implementation and test evidence for the pinned source commit. It does not prove that the same revision is deployed on the base URL you received. Before an agent can use it, delete a synthetic file in an authorized environment and confirm that the file no longer appears in file access or search results.

A token still cannot:

  • Delete bundles or saved sessions.
  • Rename a file, or save a conversation attached to a file. Conversations attached to bundles and to saved sessions can be saved.
  • Read text-extraction outputs or page dimensions.
  • Resolve a vector-search hit into page content.
  • Manage human members. Service-account provisioning is the separate source-bounded surface above.
  • Discover which organization or project it belongs to.

Design around all of these rather than probing for them. Paths outside the bearer allowlist return the same opaque unauthorized response. Route-level permission checks can instead return a concealed not-found response. Neither response is a discovery mechanism.

Failure modes, in the order you will meet them

  1. Everything returns unauthorized. Check the token prefix and length first, then the project ID, then whether the endpoint is one a token may reach at all. The bearer response is identical for malformed, unknown, revoked, and disallowed paths.
  2. A valid token gets not found. Check the live project grant. Keyline can conceal missing access and a missing resource with the same response. Do not probe for either.
  3. A file never becomes ready. Confirm the format is one of PDF, PNG, JPEG, or an Office format (DOCX, XLSX, PPTX, DOC, XLS, PPT), and that it is under the size and page limits. A rejected format fails at upload, before any processing starts.
  4. Uploading the same document twice. Duplicate names in the processing queue are rejected, and a file already being processed is locked. Treat both as "already in flight" rather than as errors to retry.
  5. Interleaved progress output looks like nonsense. It is parallel analysis. Buffer by unit identifier.
  6. The conversation was not saved. Deep read does not persist. That is a second call.
  7. An answer says nothing relevant was found. Often correct, especially on architectural, electrical, and wiring drawings, which are a confirmed failure case. Do not paper over it with a vaguer question.
  8. Error text arrives in Japanese. Some server messages are not fully translated yet. Match on status codes.

Known gaps in this preview

Stated so you can plan rather than discover:

  • Current source includes self-service, show-once key creation for project managers. Treat it as available only after the deployed preview exposes Project Settings and a synthetic create/reset check passes there.
  • The source-backed contract is HTTP. Do not depend on a command-line tool until one is delivered and verified in your preview environment.
  • No published machine-readable API schema.
  • No webhooks.
  • Be a considerate caller and back off on errors rather than retrying tightly.

Getting access

Sign up for the Public Preview from the developer page. After access is enabled, sign in with Google, open the personal project, and create the project-scoped token in Project Settings.