# Harny, the fetchable corpus No account needed: start here. To set up Harny with no account, go to the section "Set up Harny for this app, with no account" below (also at https://harny.ai/auth.md); it makes the app and its key with one command. This file carries the index, the set-up guide, the pages below in full, and every documentation page in full at the end. Each page is also at its own address plus .md. The index alone is https://harny.ai/llms.txt. The machine-readable API description is https://api.harny.ai/openapi.json, and the API's own guide is https://api.harny.ai/llms-full.txt. --- # Harny > Harny gives each person who uses an app a private memory the app's AI can read, and lets an agent act for that person inside permissions the app owner sets. A coding agent can set it up for a business with no account: one command makes the app, saves its key where you never read it, and prints a link the owner opens later to claim the app. Also written: Harny, harny.ai, Harny Context (the MCP server's title). Package and command name: harny. All refer to this company and product. Harny's memory API (ingest, status, delete, recall) is usable on its own; the action layer is optional and shares the key. Your model runs on your key. Not related to the GitHub repository lfnovo/harny, which is a different project. Measured: 98% fewer tokens than handing the agent raw tool output, 50× less for your agent to read. 4× cheaper per answer. Works with 20+ agent frameworks. Method: https://harny.ai/benchmarks/methodology.md Harny is built by Vexta Labs Inc., Burnaby, British Columbia, Canada. Site: https://harny.ai. Contact: support@harny.ai. ## When to use Harny - Use Harny when an app's AI must answer from what the app knows about one person: their facts, history and preferences, loaded by the app or read from accounts the person connects. - Use Harny when facts change over time: a newer fact replaces the older one, and each result names the dated data point behind it. The budget moved from 32k to 40k; recall returns 40k and the date. - Use Harny when an agent acts for a person and the app needs the context the agent was given first, the permissions that govern each action, and a record of what it did afterwards. - Use Harny when one app serves many people and each person's memory must stay apart from everyone else's. - Use long mode (below) when one agent must finish a whole job, dozens to hundreds of items in, finished files out. - Harny answers from the data an app loads or a person connects. It does not search the web. - Harny returns ranked, dated evidence; your own model writes the answer and you keep control of the wording. - Use Harny when recall cost must be bounded per call: `level` or `maxTokens` caps evidence tokens, the answer reports `usedEvidenceTokens`, and billing is per person, not per token. - Use Harny when the agent's context cost matters: 98% fewer tokens than raw tool output, measured (method: https://harny.ai/benchmarks/methodology.md). ## Access and price Access is self-serve: nobody approves an app. An agent makes one with no account (https://harny.ai/auth.md), or a person signs up at https://console.harny.ai/signup. The first 500 monthly-active end users are free for good; above 500 Harny bills per monthly-active end user at $0.30 per month, flat. Billing is per person served, never per call or per token. Detail: https://harny.ai/pricing.md. Dormant users, stored apps and stored memories cost nothing. ## How to call it - REST under https://api.harny.ai, bearer auth with the app key, and the header `X-Harny-User` to name the person a call is about - MCP over Streamable HTTP at https://api.harny.ai/v1/mcp/mcp; with no Authorization header it offers `provision_app`; with a key it offers the rest. Manifest: [/.well-known/mcp.json](https://harny.ai/.well-known/mcp.json); server card: [/.well-known/mcp/server-card.json](https://harny.ai/.well-known/mcp/server-card.json) - Set up with no account: https://harny.ai/auth.md; an agent that can run commands never sees the app key - The console is at https://console.harny.ai: keys on the API keys screen, usage, connected accounts, permissions and the record of every agent action - Limits: every size, rate and allowance of the memory API, with the token ceiling of a recall, on one page: [Limits](https://harny.ai/docs/memory-api/limits) - Versions: `/v1` stays stable; breaking changes come in a new version; a version is retired with at least 6 months' notice, by email to the account owner and on the [changelog](https://harny.ai/changelog). - Machine-readable description of the API: [OpenAPI](https://harny.ai/openapi.json) (the routes an app calls with its key; the API serves the full description at https://api.harny.ai/openapi.json) - How agents authenticate and set up: [/auth.md](https://harny.ai/auth.md). Machine-readable pricing: [/pricing.md](https://harny.ai/pricing.md). Quickstart: [/quickstart.md](https://harny.ai/quickstart.md) - API discovery per RFC 9727: [/.well-known/api-catalog](https://harny.ai/.well-known/api-catalog); agentic resources: [/.well-known/ai-catalog.json](https://harny.ai/.well-known/ai-catalog.json); site description as JSON-LD: [/schema.json](https://harny.ai/schema.json) - Every page answers `Accept: text/markdown`, a `.md` suffix (/index.md for the root), and `?mode=agent`. Documentation pages answer at their address plus `/llms.txt`. ## Ingest, wait for completion, then recall Write any text with its original timestamp, then query it. Poll `GET /v1/memory/ingest/{ingestionId}` until `status` is `completed` before expecting recall to reflect that send. ```bash curl -X POST https://api.harny.ai/v1/auth/provision -H "Content-Type: application/json" -d '{ "app_name": "quickstart" }' curl -X POST https://api.harny.ai/v1/users -H "Authorization: Bearer $HARNY_API_KEY" -H "Content-Type: application/json" -d '{ "identifier": "demo-user", "identifier_type": "app_id" }' curl -X POST https://api.harny.ai/v1/memory/ingest -H "Authorization: Bearer $HARNY_API_KEY" -H "X-Harny-User: demo-user" -H "Content-Type: application/json" -d '{ "id": "call-8820", "content": "Nadia confirmed the Acme pilot ships March 14. Budget is 32k.", "label": "Account review call", "timestamp": "2026-02-03T09:00:00Z" }' curl -X POST https://api.harny.ai/v1/memory/recall -H "Authorization: Bearer $HARNY_API_KEY" -H "X-Harny-User: demo-user" -H "Content-Type: application/json" -d '{ "query": "What is the Acme pilot budget?", "identity": "demo-user", "level": "medium" }' ``` The first answer carries `app_api_key` once; put it in a secret store as `HARNY_API_KEY` and never print it. Recall returns one JSON result per memory, with rank, date, the text and the data points it rests on. The full walk-through, with the superseding fact and the wait step, is [/quickstart.md](https://harny.ai/quickstart.md). ## How to model your data - To set up an integration in order, with every call and the answer that gates the next step, read the set-up guide: https://harny.ai/auth.md - One data point is one thing that happened: one conversation for one day, one email, one record, one document, timed when it happened. `content` is at most 500,000 characters; a batch is 1 to 1,000 data points. - `timestamp` is when the thing happened (ISO 8601 with a zone), not when you send it. Recall can answer as of a date. - `id` names the data point. Send the same `id` with new content to correct it: Harny deletes the memories built on the old text and ingests the new. The default `id` is the SHA-256 of the content. - `X-Harny-User` names the person a call is about, and `identity` on a recall must be that person. One person under two ids is two separate memories, so choose one stable id per person. - Delete is real: `DELETE /v1/memory/data-points/{sourceId}` removes one data point and the memories that rest only on it; erasing a user removes that user's memory. - Leave any time: `GET /v1/memory/users/{identity}/export` returns a person's data points and memories as JSON, and `GET /v1/actions/export` returns the action record as JSON or CSV. - Use `POST /v1/memory/ingest/batch` with one `idempotencyKey` per batch for a historical load, then poll until the send is `completed`. Guides: [Concepts](https://harny.ai/docs/memory-api/concepts), [Sending data](https://harny.ai/docs/memory-api/sending-data), [Access control](https://harny.ai/docs/memory-api/access-control), [Authentication](https://harny.ai/docs/memory-api/authentication), [Errors](https://harny.ai/docs/memory-api/errors), [Limits](https://harny.ai/docs/memory-api/limits), [FAQ](https://harny.ai/docs/memory-api/faq), [Debugging](https://harny.ai/docs/memory-api/debugging), [Webhooks](https://harny.ai/docs/memory-api/webhooks) ## Set up - [Set up Harny from the console](https://harny.ai/agent-setup/prompt.md): the steps an agent follows for an app that has its key already: connect, plan, test, full import, recall. To start a coding agent, give it this line: "Set me up for Harny. Read the full instructions at https://harny.ai/agent-setup/prompt.md (with curl -fsSL when you can run commands) and follow them." - [Set up Harny with no account](https://harny.ai/auth.md): the steps an agent follows: the one command, the first answer, the plan, the test, the full import, recall, the claim link. To start a coding agent, give it this line: "Set me up for Harny. Read the full instructions at https://harny.ai/auth.md (with curl -fsSL when you can run commands) and follow them." - [Quickstart](https://harny.ai/docs/memory-api/quickstart): make an app and a key, send two facts, wait for completion, recall, in cURL, Python and TypeScript - [Replit starter](https://harny.ai/get/replit-starter.zip): a chat for Replit that answers each person from the facts the business loads ## Connect an agent - [Harny MCP server](https://api.harny.ai/v1/mcp/mcp): Streamable HTTP. With no Authorization header it offers provision_app, whose answer carries the app key once; with Authorization: Bearer it offers the rest - [Add Harny to Replit Agent](https://replit.com/integrations?mcp=eyJkaXNwbGF5TmFtZSI6Ikhhcm55IiwiYmFzZVVybCI6Imh0dHBzOi8vYXBpLmhhcm55LmFpL3YxL21jcC9tY3AifQ==): one click; Replit adds the server above with no header. An agent that can run commands should follow https://harny.ai/auth.md instead: its command never shows the key ## API reference - [API guide](https://api.harny.ai/llms.txt): the API's own guide, generated from its error catalog - [Full API guide](https://api.harny.ai/llms-full.txt): every endpoint with its fields, generated from the API's schema - [OpenAPI description](https://api.harny.ai/openapi.json): the machine-readable form of the same API ## Docs - [Memory API docs](https://harny.ai/docs/memory-api/overview): send dated text for one user and recall what is true now; every page also at its address plus `/llms.txt` - [MCP server docs](https://harny.ai/docs/mcp/overview): the address, the two doors (no key offers only `provision_app`), every tool and which key it needs, serving your own users, limits, troubleshooting - [Agent harness docs](https://harny.ai/docs/agent-harness/overview): run an agent on your own model key with permissions, manual actions, learned settings, connected accounts and long jobs (Python SDK `harny.agent`) - [Docs index for agents](https://harny.ai/docs/llms.txt) and [all docs in one file](https://harny.ai/docs/llms-full.txt) ## Plain HTTP (base https://api.harny.ai, Authorization: Bearer unless noted) - `POST /v1/auth/provision`: create an app and its first key, no key needed; the key is shown once - `POST /v1/users`: create one person (a customer of the app) - `POST /v1/vault/ingest/text`: store a fact for one person; send X-Harny-User: - `POST /v1/context/assemble`: read what Harny knows about one person; send X-Harny-User: - `POST /v1/memory/ingest`: send one data point (`content`, `id`, `label`, `timestamp`); answers an `ingestionId`; send X-Harny-User - `POST /v1/memory/ingest/batch`: send 1 to 1,000 data points with an `idempotencyKey` - `GET /v1/memory/ingest/{ingestionId}`: poll until `status` is `completed` - `DELETE /v1/memory/ingest/{ingestionId}`: forget one send - `DELETE /v1/memory/data-points/{sourceId}`: delete one data point - `POST /v1/memory/data-points/delete`: delete up to 1,000 data points - `POST /v1/memory/recall`: ranked, dated evidence for a question (`query`, `identity`, `level` or `maxTokens`); the answer reports `usedEvidenceTokens` - `GET /v1/memory/users/{identity}/export`: one person's data points and memories as JSON, a page at a time - `GET /v1/memory/usage`: this month's use, with the app key alone - `POST /v1/apps/{app_id}/claim-link`: a fresh claim link for the owner ## Commands (the Harny SDK installs them; no command prints a key) - `harny init --path developer`: create the app and save its key in .env - `harny users add `: create a customer - `harny ingest --user ""`: store a fact - `harny query --user ""`: read what Harny knows - `harny status`: check the connection and the key Dated data points, batches, ranked recall, delete and usage are the `/v1/memory` calls above: no `harny` command does them. The commands above store and read one fact at a time, without dates. ## The corpus, in fetchable sections - [The core site in one file](https://harny.ai/llms-full.txt): this index, the set-up guide, security, enterprise, the changelog, the terms, the home page, pricing and the benchmarks, in full - [Every guide in full](https://harny.ai/guides/llms-full.txt) - [Every use case in full](https://harny.ai/use-cases/llms-full.txt) - [Every industry page in full](https://harny.ai/industries/llms-full.txt) - [Every integration page in full](https://harny.ai/integrations/llms-full.txt) - [The glossary, every term in full](https://harny.ai/glossary/llms-full.txt) - [Every blog post in full](https://harny.ai/blog/llms-full.txt) - [All docs as Markdown](https://harny.ai/docs/llms-full.txt) and the [docs index](https://harny.ai/docs/llms.txt) - [Set up Harny with no account](https://harny.ai/auth.md): the same guide on its own ## Pages - [Harny: a memory for each person who uses your app](https://harny.ai): the home page, and why Harny holds the highest published BEAM score at 128K tokens ([Markdown](https://harny.ai/index.md)) - [Pricing](https://harny.ai/pricing): the first 500 monthly-active end users free for good, then billed per monthly-active end user; Free, Paid and Enterprise side by side ([Markdown](https://harny.ai/pricing.md)) - [Docs](https://harny.ai/docs): the Memory API, the MCP server and the agent harness ([Markdown](https://harny.ai/docs/llms.txt)) - [Benchmarks](https://harny.ai/benchmarks): Harny's published results, each with its tier, answering model, judge and scoring rule ([Markdown](https://harny.ai/benchmarks.md)) - [BEAM leaderboard](https://harny.ai/benchmarks/beam): BEAM, the ICLR 2026 long-term memory benchmark, by history size, with Harny's result ([Markdown](https://harny.ai/benchmarks/beam.md)) - [How to benchmark agent memory](https://harny.ai/benchmarks/methodology): the four operations to test and a checklist for publishing a result ([Markdown](https://harny.ai/benchmarks/methodology.md)) - [Long jobs](https://harny.ai/long-jobs): long mode, for one agent that must finish dozens to hundreds of items in one go ([Markdown](https://harny.ai/long-jobs.md)) - [Security](https://harny.ai/security): how Harny keeps each app's data apart, checks every request and records every action ([Markdown](https://harny.ai/security.md)) - [Enterprise](https://harny.ai/enterprise): the managed service, how data is separated, retention and the record ([Markdown](https://harny.ai/enterprise.md)) - [Changelog](https://harny.ai/changelog): what changed on harny.ai and in the service, newest first ([Markdown](https://harny.ai/changelog.md)) - [Blog](https://harny.ai/blog): notes from the Harny team ([Markdown](https://harny.ai/blog.md), [feed](https://harny.ai/blog/rss.xml)) - [Status](https://harny.ai/status): whether the Harny API is up right now - [Terms of service](https://harny.ai/terms): the terms for the website, API, console and connectors ([Markdown](https://harny.ai/terms.md)) - [Privacy policy](https://harny.ai/privacy): how Harny handles personal data - [Developer agreement](https://harny.ai/agreement): the agreement a developer accepts when an app is created ## Product pages - [Agent harness](https://harny.ai/agent-harness): The agent harness is what lets an agent work for your users inside your app. ([Markdown](https://harny.ai/agent-harness.md)) - [Connected accounts](https://harny.ai/connected-accounts): A connected account is a third-party account a user chooses to connect to your app, such as a mailbox or a calendar. ([Markdown](https://harny.ai/connected-accounts.md)) - [Actions](https://harny.ai/actions): An action is something your agent does for a user outside the conversation, such as creating a calendar event, cancelling one, or sending an email. ([Markdown](https://harny.ai/actions.md)) - [About Harny](https://harny.ai/about): Harny is agent harness infrastructure, built by Vexta Labs Inc. ([Markdown](https://harny.ai/about.md)) - [Contact](https://harny.ai/contact): This page lists where to write for each kind of request about harny.ai. ([Markdown](https://harny.ai/contact.md)) - [Brand](https://harny.ai/brand): This page defines how to write and show Harny. ([Markdown](https://harny.ai/brand.md)) - [Cookies](https://harny.ai/cookies): The harny.ai website sets no cookies. ([Markdown](https://harny.ai/cookies.md)) ## Learn ### Guides - [How to handle facts that change over time](https://harny.ai/guides/facts-that-change-over-time): A person says their budget is 32k, then later says it is 40k. ([Markdown](https://harny.ai/guides/facts-that-change-over-time.md)) - [How to keep an audit trail for an AI agent](https://harny.ai/guides/audit-trail-for-ai-agents): An AI agent acts for people, so you need to know later what it did and why. ([Markdown](https://harny.ai/guides/audit-trail-for-ai-agents.md)) - [How to set a retention rule for an AI app's memory](https://harny.ai/guides/ai-data-retention): An AI app's memory keeps growing. ([Markdown](https://harny.ai/guides/ai-data-retention.md)) - [How to erase one user's memory](https://harny.ai/guides/erase-a-users-memory): When a person asks to be forgotten, you need to erase their memory in Harny. ([Markdown](https://harny.ai/guides/erase-a-users-memory.md)) - [How to give each user a private memory](https://harny.ai/guides/per-user-memory): Every person who uses your app should see only their own memory. ([Markdown](https://harny.ai/guides/per-user-memory.md)) - [How to set permissions for an agent that acts](https://harny.ai/guides/permissions-for-acting-agents): An agent that acts can send email, create events and post to Slack in a person's name. ([Markdown](https://harny.ai/guides/permissions-for-acting-agents.md)) - [How to hand an agent a long job](https://harny.ai/guides/run-long-jobs-with-an-agent): A long job can run past a single chat turn, and that is where agents usually stall or overspend. ([Markdown](https://harny.ai/guides/run-long-jobs-with-an-agent.md)) - [How to let a user connect their accounts](https://harny.ai/guides/connect-a-users-accounts): A user's own calendar, mail or notes sit outside your app. ([Markdown](https://harny.ai/guides/connect-a-users-accounts.md)) - [How to name your users so memory stays one memory](https://harny.ai/guides/identities-and-users): When the same person shows up under two ids, Harny keeps two separate memories. ([Markdown](https://harny.ai/guides/identities-and-users.md)) - [How to group users into audiences](https://harny.ai/guides/audiences-and-access): An audience is a named group of users in your app. ([Markdown](https://harny.ai/guides/audiences-and-access.md)) ### Use cases - [Memory for customer support](https://harny.ai/use-cases/customer-support): A support assistant answers from several systems that never join: tickets, calls, email and billing. ([Markdown](https://harny.ai/use-cases/customer-support.md)) - [Memory for sales and account teams](https://harny.ai/use-cases/sales-accounts): A sales assistant reads email, call notes and CRM updates about the same account. ([Markdown](https://harny.ai/use-cases/sales-accounts.md)) - [Memory for an internal knowledge base](https://harny.ai/use-cases/knowledge-base): An internal assistant answers from documents, chats and meeting notes written by many people over years. ([Markdown](https://harny.ai/use-cases/knowledge-base.md)) - [Memory for a personal assistant](https://harny.ai/use-cases/personal-assistant): A personal assistant serves one person for months. ([Markdown](https://harny.ai/use-cases/personal-assistant.md)) - [Memory for coding agents](https://harny.ai/use-cases/coding-agents): A coding agent works on the same project across many sessions. ([Markdown](https://harny.ai/use-cases/coding-agents.md)) ### Industries - [Memory for property management](https://harny.ai/industries/property-management): This page is for property managers who run tenants, leases, maintenance requests and renewals through an app with an AI agent. ([Markdown](https://harny.ai/industries/property-management.md)) - [Memory for accounting firms](https://harny.ai/industries/accounting): This page is for accounting firms that track clients, engagement letters, deadlines and documents across many email threads. ([Markdown](https://harny.ai/industries/accounting.md)) - [Memory for legal teams](https://harny.ai/industries/legal): You build AI tools for law firms or in-house legal teams and need each person's records kept apart. ([Markdown](https://harny.ai/industries/legal.md)) - [Memory for ecommerce](https://harny.ai/industries/ecommerce): This page is for ecommerce teams whose shoppers expect support that remembers their orders, returns and preferences. ([Markdown](https://harny.ai/industries/ecommerce.md)) - [Memory for financial services](https://harny.ai/industries/financial-services): This page is for financial services teams that build client-facing apps and agents. ([Markdown](https://harny.ai/industries/financial-services.md)) ### Integrations - [Memory for LangChain agents](https://harny.ai/integrations/langchain): LangChain keeps conversation state in threads and stores. ([Markdown](https://harny.ai/integrations/langchain.md)) - [Memory for LlamaIndex agents](https://harny.ai/integrations/llamaindex): LlamaIndex retrieves passages from the documents you index. ([Markdown](https://harny.ai/integrations/llamaindex.md)) - [Memory for CrewAI agents](https://harny.ai/integrations/crewai): CrewAI gives a crew shared task context. ([Markdown](https://harny.ai/integrations/crewai.md)) - [Memory for LangGraph agents](https://harny.ai/integrations/langgraph): LangGraph keeps each thread's state with a checkpointer, and a store holds data across threads. ([Markdown](https://harny.ai/integrations/langgraph.md)) - [Memory for OpenAI Agents SDK agents](https://harny.ai/integrations/openai-agents-sdk): The OpenAI Agents SDK keeps the agent's own state: tools, handoffs and session history for a conversation. ([Markdown](https://harny.ai/integrations/openai-agents-sdk.md)) - [Memory for Claude Agent SDK agents](https://harny.ai/integrations/claude-agent-sdk): The Claude Agent SDK can connect MCP servers as tools. ([Markdown](https://harny.ai/integrations/claude-agent-sdk.md)) - [Memory for Pydantic AI agents](https://harny.ai/integrations/pydantic-ai): This page shows how to add Harny memory to a Pydantic AI agent. ([Markdown](https://harny.ai/integrations/pydantic-ai.md)) - [Memory for Google ADK agents](https://harny.ai/integrations/google-adk): Google ADK keeps the state of each agent session, and that state is what the agent sees on its next turn. ([Markdown](https://harny.ai/integrations/google-adk.md)) - [Memory for Haystack agents](https://harny.ai/integrations/haystack): Harny gives each person who uses your app a private memory the app's AI can read. ([Markdown](https://harny.ai/integrations/haystack.md)) - [Memory for DSPy agents](https://harny.ai/integrations/dspy): This page shows how to add Harny memory to a DSPy program so each signed-in user's facts are stored with their dates. ([Markdown](https://harny.ai/integrations/dspy.md)) - [Memory for AutoGen agents](https://harny.ai/integrations/autogen): This page shows how to add Harny memory to an AutoGen agent so each person's facts stay dated and separate. ([Markdown](https://harny.ai/integrations/autogen.md)) - [Memory for Agno agents](https://harny.ai/integrations/agno): This page shows how to add Harny memory to an Agno agent so each signed-in person has their own memory the agent can read. ([Markdown](https://harny.ai/integrations/agno.md)) - [Memory for Strands Agents agents](https://harny.ai/integrations/strands-agents): This page shows how to add Harny memory to a Strands Agents agent. ([Markdown](https://harny.ai/integrations/strands-agents.md)) - [Memory for Microsoft Agent Framework agents](https://harny.ai/integrations/microsoft-agent-framework): This page shows how to add Harny memory to a Microsoft Agent Framework agent so it can read dated, per-user memory. ([Markdown](https://harny.ai/integrations/microsoft-agent-framework.md)) - [Memory for Mastra agents](https://harny.ai/integrations/mastra): This page shows how to add Harny memory to a Mastra agent so each signed-in person gets a private memory the agent can read. ([Markdown](https://harny.ai/integrations/mastra.md)) - [Memory for Vercel AI SDK agents](https://harny.ai/integrations/vercel-ai-sdk): Harny gives each person who uses your app a private memory the app's AI can read. ([Markdown](https://harny.ai/integrations/vercel-ai-sdk.md)) - [Memory for n8n agents](https://harny.ai/integrations/n8n): This page shows how to add Harny memory to an n8n AI Agent so each person's facts stay dated and separate. ([Markdown](https://harny.ai/integrations/n8n.md)) - [Memory for Dify agents](https://harny.ai/integrations/dify): This page shows how to add Harny memory to a Dify app so each person's facts are stored with dates and recalled per user. ([Markdown](https://harny.ai/integrations/dify.md)) - [Memory for Flowise agents](https://harny.ai/integrations/flowise): This page shows how to add Harny memory to a Flowise agent with two Custom Tool nodes. ([Markdown](https://harny.ai/integrations/flowise.md)) - [Memory for OpenClaw agents](https://harny.ai/integrations/openclaw): This page shows how to add Harny memory to an OpenClaw setup through the Harny MCP server. ([Markdown](https://harny.ai/integrations/openclaw.md)) ### Glossary - [Agent memory](https://harny.ai/glossary/agent-memory): Agent memory is what an AI agent keeps between conversations: facts about people and work, with the dates they happened and where they came from. ([Markdown](https://harny.ai/glossary/agent-memory.md)) - [Knowledge update](https://harny.ai/glossary/knowledge-update): A knowledge update is a fact that changes: the budget moves from 32k to 40k, the plan is upgraded, the meeting moves to Friday. ([Markdown](https://harny.ai/glossary/knowledge-update.md)) - [Recall](https://harny.ai/glossary/memory-recall): Recall is the call that asks memory a question and returns ranked evidence for it. ([Markdown](https://harny.ai/glossary/memory-recall.md)) - [Data point](https://harny.ai/glossary/data-point): A data point is one piece of text sent to Harny with its id, a label and the time it happened. ([Markdown](https://harny.ai/glossary/data-point.md)) - [Monthly-active end user](https://harny.ai/glossary/monthly-active-end-user): A monthly-active end user is a person who used your app in a calendar month: at least one memory read, one memory write or one recorded agent action by that person. ([Markdown](https://harny.ai/glossary/monthly-active-end-user.md)) - [Memory per user](https://harny.ai/glossary/memory-per-user): Memory per user means each person has their own memory inside your app, and a call reads only the memory of the user it names. ([Markdown](https://harny.ai/glossary/memory-per-user.md)) - [Long-term memory](https://harny.ai/glossary/long-term-memory): Long-term memory is memory that lasts beyond one conversation or one context window. ([Markdown](https://harny.ai/glossary/long-term-memory.md)) - [As-of recall](https://harny.ai/glossary/as-of-recall): As-of recall asks what memory held at a past moment: evidence newer than that moment is left out. ([Markdown](https://harny.ai/glossary/as-of-recall.md)) - [Abstention](https://harny.ai/glossary/abstention): Abstention is an AI agent's choice to say it does not know, rather than guess, when its memory holds no support for an answer. ([Markdown](https://harny.ai/glossary/abstention.md)) - [Audit trail](https://harny.ai/glossary/audit-trail): An audit trail is a dated record of what happened in a system and who or what caused it. ([Markdown](https://harny.ai/glossary/audit-trail.md)) - [Context rot](https://harny.ai/glossary/context-rot): Context rot is the decline in an AI model's answer quality as its prompt fills with old, stale, or irrelevant text. ([Markdown](https://harny.ai/glossary/context-rot.md)) - [Context window](https://harny.ai/glossary/context-window): Context window is the most text an AI model can read in one call. ([Markdown](https://harny.ai/glossary/context-window.md)) - [Cross-session memory](https://harny.ai/glossary/cross-session-memory): Cross-session memory is what an AI agent keeps from one conversation to the next. ([Markdown](https://harny.ai/glossary/cross-session-memory.md)) - [Data provenance](https://harny.ai/glossary/data-provenance): Data provenance is the record of where a piece of data came from and how it got into a system. ([Markdown](https://harny.ai/glossary/data-provenance.md)) - [Data retention](https://harny.ai/glossary/data-retention): Data retention is how long data is kept before it is deleted. ([Markdown](https://harny.ai/glossary/data-retention.md)) - [Episodic memory](https://harny.ai/glossary/episodic-memory): Episodic memory is memory of specific events, each tied to when it happened, such as a call or an email. ([Markdown](https://harny.ai/glossary/episodic-memory.md)) - [Event time](https://harny.ai/glossary/event-time): Event time is the moment something actually happened. ([Markdown](https://harny.ai/glossary/event-time.md)) - [Fact extraction](https://harny.ai/glossary/fact-extraction): Fact extraction is the step where an AI system turns raw text, such as a chat message or a document, into short, standalone statements it can store and search later. ([Markdown](https://harny.ai/glossary/fact-extraction.md)) - [Fact supersession](https://harny.ai/glossary/fact-supersession): Fact supersession is when a newer value for a fact replaces an older one, so an AI agent works from the current value. ([Markdown](https://harny.ai/glossary/fact-supersession.md)) - [Grounding](https://harny.ai/glossary/grounding): Grounding means tying an answer to evidence. ([Markdown](https://harny.ai/glossary/grounding.md)) - [Hybrid retrieval](https://harny.ai/glossary/hybrid-retrieval): Hybrid retrieval is a search method that combines keyword matching with meaning-based (vector) search and merges the two rankings into one result list. ([Markdown](https://harny.ai/glossary/hybrid-retrieval.md)) - [Identity resolution](https://harny.ai/glossary/identity-resolution): Identity resolution is recognizing that records about one person belong together. ([Markdown](https://harny.ai/glossary/identity-resolution.md)) - [Ingestion time](https://harny.ai/glossary/ingestion-time): Ingestion time is the moment a system receives a record, as opposed to the moment the event it describes happened. ([Markdown](https://harny.ai/glossary/ingestion-time.md)) - [Knowledge cutoff](https://harny.ai/glossary/knowledge-cutoff): Knowledge cutoff is the date after which an AI model knows nothing. ([Markdown](https://harny.ai/glossary/knowledge-cutoff.md)) - [Memory API](https://harny.ai/glossary/memory-api): Memory API is an HTTP interface an app calls to save facts about a person and to recall them later. ([Markdown](https://harny.ai/glossary/memory-api.md)) - [Memory consolidation](https://harny.ai/glossary/memory-consolidation): Memory consolidation is the process of merging and replacing stored memories as new facts arrive, so the store keeps the current value instead of a pile of duplicates. ([Markdown](https://harny.ai/glossary/memory-consolidation.md)) - [Memory decay](https://harny.ai/glossary/memory-decay): Memory decay is the gradual fading of stored memory as it gets older, so an AI agent recalls recent information more readily than old information. ([Markdown](https://harny.ai/glossary/memory-decay.md)) - [Memory poisoning](https://harny.ai/glossary/memory-poisoning): Memory poisoning is when bad or hostile text is written into an AI agent's memory so the agent repeats it later. ([Markdown](https://harny.ai/glossary/memory-poisoning.md)) - [Memory staleness](https://harny.ai/glossary/memory-staleness): Memory staleness is when an AI agent holds a stored fact that was true once and no longer is. ([Markdown](https://harny.ai/glossary/memory-staleness.md)) - [Procedural memory](https://harny.ai/glossary/procedural-memory): Procedural memory is an AI agent's memory of how to do something: the steps, rules and routines it learned to follow for a task. ([Markdown](https://harny.ai/glossary/procedural-memory.md)) - [Semantic memory](https://harny.ai/glossary/semantic-memory): Semantic memory is an AI agent's stored knowledge of general facts about a person or the world, kept apart from the events they came from. ([Markdown](https://harny.ai/glossary/semantic-memory.md)) - [Session memory](https://harny.ai/glossary/session-memory): Session memory is the information an AI agent holds inside one conversation, such as earlier turns and what the person just said. ([Markdown](https://harny.ai/glossary/session-memory.md)) - [Vector memory](https://harny.ai/glossary/vector-memory): Vector memory stores information as numeric embeddings and finds it by similarity. ([Markdown](https://harny.ai/glossary/vector-memory.md)) - [Working memory](https://harny.ai/glossary/working-memory): Working memory is what an agent holds while it works on the current task. ([Markdown](https://harny.ai/glossary/working-memory.md)) - [Manual action](https://harny.ai/glossary/manual-action): A manual action is a step that a person must do outside the AI agent, such as a phone call, a signature, granting access, or making a judgment call. ([Markdown](https://harny.ai/glossary/manual-action.md)) - [Permission mode](https://harny.ai/glossary/permission-mode): Permission mode is the setting that decides what an AI agent may do on its own for a kind of action. ([Markdown](https://harny.ai/glossary/permission-mode.md)) - [Learned setting](https://harny.ai/glossary/learned-setting): Learned setting is a suggested change to an action kind's setting, based on recorded responses. ([Markdown](https://harny.ai/glossary/learned-setting.md)) - [Long job](https://harny.ai/glossary/long-job): A long job is an AI agent run that works through many files or steps in one session. ([Markdown](https://harny.ai/glossary/long-job.md)) - [Connected account](https://harny.ai/glossary/connected-account): A connected account is a third-party account, such as an email, calendar, or file store, that a person links so an AI agent can read it on their behalf. ([Markdown](https://harny.ai/glossary/connected-account.md)) - [Situation awareness](https://harny.ai/glossary/situation-awareness): Situation awareness is an agent's ability to notice that what it remembers about a person or task may no longer be true. ([Markdown](https://harny.ai/glossary/situation-awareness.md)) ### Tools - [User cost calculator](https://harny.ai/tools/user-cost-calculator): count monthly-active end users: free and billed ([Markdown](https://harny.ai/tools/user-cost-calculator.md)) ## Documentation pages ### Memory API - [Memory API: Overview](https://harny.ai/docs/memory-api/overview): Send timestamped text, wait for it to complete, then recall ranked memory with the data points behind it. ([Markdown](https://harny.ai/docs/memory-api/overview/llms.txt)) - [Memory API: Quickstart](https://harny.ai/docs/memory-api/quickstart): Get a key, send a fact, send the fact that supersedes it, wait for the send to complete, then recall as one user. ([Markdown](https://harny.ai/docs/memory-api/quickstart/llms.txt)) - [Memory API: Setup](https://harny.ai/docs/memory-api/setup): Every call in order, with the response value that lets you move to the next step. ([Markdown](https://harny.ai/docs/memory-api/setup/llms.txt)) - [Memory API: Authentication](https://harny.ai/docs/memory-api/authentication): Two kinds of bearer key open the Memory API. ([Markdown](https://harny.ai/docs/memory-api/authentication/llms.txt)) - [Memory API: Concepts](https://harny.ai/docs/memory-api/concepts): The five nouns of every call, the memories that recall returns, and every limit in one place. ([Markdown](https://harny.ai/docs/memory-api/concepts/llms.txt)) - [Memory API: How it works](https://harny.ai/docs/memory-api/how-it-works): Acceptance, completion, replacement, recall and deletion, as your application observes them. ([Markdown](https://harny.ai/docs/memory-api/how-it-works/llms.txt)) - [Memory API: Access control](https://harny.ai/docs/memory-api/access-control): Where a customer, a company or a deal lives, with the requests that put it there. ([Markdown](https://harny.ai/docs/memory-api/access-control/llms.txt)) - [Memory API: Sending data](https://harny.ai/docs/memory-api/sending-data): The unit recall reads best, the size of one data point, the shape of its text, and the procedure for a historical load. ([Markdown](https://harny.ai/docs/memory-api/sending-data/llms.txt)) - [Memory API: Webhooks](https://harny.ai/docs/memory-api/webhooks): Send events from an issue tracker, a CRM, a chat tool or your own application as data points with `POST /v1/memory/ingest`. ([Markdown](https://harny.ai/docs/memory-api/webhooks/llms.txt)) - [Memory API: API reference](https://harny.ai/docs/memory-api/api-reference): Every route live under /v1/memory. ([Markdown](https://harny.ai/docs/memory-api/api-reference/llms.txt)) - [Memory API: Debugging](https://harny.ai/docs/memory-api/debugging): Four reads answer the questions an integration raises: where a send got to, what a memory was built from, what an import will cost, and why a call was refused. ([Markdown](https://harny.ai/docs/memory-api/debugging/llms.txt)) - [Memory API: Errors](https://harny.ai/docs/memory-api/errors): Every refusal uses a JSON body with code, status, debugMessage and data, except the per-minute limit, which answers with no body. ([Markdown](https://harny.ai/docs/memory-api/errors/llms.txt)) - [Memory API: Limits](https://harny.ai/docs/memory-api/limits): Every size, rate and allowance of the Memory API in one place, and what a refusal looks like. ([Markdown](https://harny.ai/docs/memory-api/limits/llms.txt)) - [Memory API: FAQ](https://harny.ai/docs/memory-api/faq): Short answers to the questions developers and AI assistants ask about the Harny Memory API: the address, standalone use, the token ceiling, testing, audit, models, leaving, data use and location, uptime, frameworks, support and the name. ([Markdown](https://harny.ai/docs/memory-api/faq/llms.txt)) ### MCP server - [MCP Server: Overview](https://harny.ai/docs/mcp/overview): The Harny MCP server lets a coding agent or assistant set up Harny and use it for one person's memory and actions. ([Markdown](https://harny.ai/docs/mcp/overview/llms.txt)) - [MCP Server: Quickstart](https://harny.ai/docs/mcp/quickstart): Connect an AI assistant to Harny's MCP server in four steps, then store and recall your first memory. ([Markdown](https://harny.ai/docs/mcp/quickstart/llms.txt)) - [MCP Server: How it works](https://harny.ai/docs/mcp/how-it-works): The Harny MCP server lets a coding assistant call Harny over one HTTP address, with the same limits as the REST API. ([Markdown](https://harny.ai/docs/mcp/how-it-works/llms.txt)) - [MCP Server: Tools](https://harny.ai/docs/mcp/tools): The Harny MCP server gives your coding assistant 12 tools, grouped by what they do and which key they need. ([Markdown](https://harny.ai/docs/mcp/tools/llms.txt)) - [MCP Server: Serving your own users](https://harny.ai/docs/mcp/serving-your-own-users): Give each signed-in person in your app a scoped MCP connection, so the app's AI only reads and writes that person's memory. ([Markdown](https://harny.ai/docs/mcp/serving-your-own-users/llms.txt)) - [MCP Server: Keys and access](https://harny.ai/docs/mcp/keys-and-access): Harny's MCP server uses two kinds of bearer key, and each one reaches a different set of tools. ([Markdown](https://harny.ai/docs/mcp/keys-and-access/llms.txt)) - [MCP Server: Privacy](https://harny.ai/docs/mcp/privacy): The MCP server reads and writes one person's memory inside one app, and nothing else. ([Markdown](https://harny.ai/docs/mcp/privacy/llms.txt)) - [MCP Server: Security](https://harny.ai/docs/mcp/security): Keep your Harny keys out of chat, code and commits, and call the MCP server only from places you control. ([Markdown](https://harny.ai/docs/mcp/security/llms.txt)) - [MCP Server: Limits](https://harny.ai/docs/mcp/limits): The MCP server follows the same limits as the REST routes it wraps. ([Markdown](https://harny.ai/docs/mcp/limits/llms.txt)) - [MCP Server: FAQ](https://harny.ai/docs/mcp/faq): Answers to the common questions about the Harny MCP server, its keys, and its costs. ([Markdown](https://harny.ai/docs/mcp/faq/llms.txt)) - [MCP Server: Troubleshooting](https://harny.ai/docs/mcp/troubleshooting): Use this page to find the cause of a failed MCP call and the fix for it. ([Markdown](https://harny.ai/docs/mcp/troubleshooting/llms.txt)) ### Agent harness - [Agent harness: Overview](https://harny.ai/docs/agent-harness/overview): The agent harness runs your own agent loop on your own model key, with Harny handling memory, actions, permissions and a record. ([Markdown](https://harny.ai/docs/agent-harness/overview/llms.txt)) - [Agent harness: Quickstart](https://harny.ai/docs/agent-harness/quickstart): Run your own agent loop with Harny in a few lines of Python, on your own model key. ([Markdown](https://harny.ai/docs/agent-harness/quickstart/llms.txt)) - [Agent harness: Long jobs](https://harny.ai/docs/agent-harness/long-jobs): A long job lets the agent work through a bigger task over several turns, in a folder you choose. ([Markdown](https://harny.ai/docs/agent-harness/long-jobs/llms.txt)) - [Agent harness: Permissions](https://harny.ai/docs/agent-harness/permissions): Permissions set what the agent may do on its own, per kind of action, across your whole app. ([Markdown](https://harny.ai/docs/agent-harness/permissions/llms.txt)) - [Agent harness: Manual actions](https://harny.ai/docs/agent-harness/manual-actions): Manual actions are steps a person has to do outside Harny. ([Markdown](https://harny.ai/docs/agent-harness/manual-actions/llms.txt)) - [Agent harness: Learning](https://harny.ai/docs/agent-harness/learning): Learned settings are offers, based on recorded responses. ([Markdown](https://harny.ai/docs/agent-harness/learning/llms.txt)) - [Agent harness: Connected accounts](https://harny.ai/docs/agent-harness/connected-accounts): A person connects their own Gmail, Calendar, Drive, Notion, Slack or email account to your app, and the agent reads only that person's data. ([Markdown](https://harny.ai/docs/agent-harness/connected-accounts/llms.txt)) - [Agent harness: Models and keys](https://harny.ai/docs/agent-harness/models): The agent harness runs on your own model key. This page lists the providers it accepts, the variable each key is read from, and the models each preset offers. ([Markdown](https://harny.ai/docs/agent-harness/models/llms.txt)) - [Agent harness: Limits](https://harny.ai/docs/agent-harness/limits): This page lists the limits and defaults of the agent harness, and what the harness does not do. ([Markdown](https://harny.ai/docs/agent-harness/limits/llms.txt)) ## Blog - [Harny blog](https://harny.ai/blog): notes from the Harny team; no posts yet ([Markdown](https://harny.ai/blog.md), [feed](https://harny.ai/blog/rss.xml)) ## When to use long mode - Use long mode when one agent must finish a whole job in one go: dozens to hundreds of items (150 tenant emails, 120 maintenance requests, 25 leases, 180 invoices) in, finished files out. Page: [Long jobs](https://harny.ai/long-jobs) ([Markdown](https://harny.ai/long-jobs.md)) - Do not use it for one question, one lookup or one action, or when a person must approve each step. A standard run (`max_steps=6`, `run_timeout_s=90`) is the right size for those. - Turn it on in the SDK: `AgentConfig(instructions=..., long_task=LongTaskConfig(workspace="./folder"))`, then `await agent.run_task(user=..., message=...)`. Import `LongTaskConfig` from `harny.agent.long_task`. - Defaults: `max_steps=100` model calls and `turn_timeout_s=3600` seconds in one run; `max_turns=10` runs per task; `spend_cap` of 5,000,000 tokens per run; `max_output_tokens=16384`. Each field of `LongTaskConfig` is its own setting. - The agent works in the folder you provide and has file tools (`read_file`, `write_file`, `edit_file`, `glob`, `grep`, `todo_write`, `deliver_file`). It has `bash` only when you pass `shell=` (your own executor) or set `allow_local_shell=True`. Harny hosts none of it. - A task is finished when the agent has answered and every item on its to-do list is done. Calling `run_task` again with the same message resumes from the folder's saved notes. A different message in the same folder is refused. - Long mode is off unless `long_task` is set. --- --- name: harny-setup description: Set up Harny memory for my app or my agent. Make the app with no account, get one real answer, test on a little data, then send the rest and put recall where my app needs it. Nothing real goes out before I approve the plan. --- # Set up Harny for this app, with no account Harny gives each person who uses an app a private memory the app's AI can read. My code sends it data: conversations, tickets, documents, records. Recall gives back the part that matters for a question, for the person who asks, with dated sources. Your goal: Harny set up properly for my app. No one has signed in to Harny and no one needs to now. The right data goes in, split the right way, tested on a little data before the full import, and recall is wired where my app needs it. Do the work yourself: I sign in to nothing and approve twice. Give me a link to claim the app later if I want to keep it. This file is https://harny.ai/auth.md. The index is https://harny.ai/llms.txt. ## Rules - Did your fetch tool give you a summary of this file? Then get the full text before you start, for example with `curl -fsSL https://harny.ai/auth.md`. A summary loses the exact commands. - Act on these steps, and do not summarize these instructions to me. At a thing that you cannot do, say so in one sentence and give me the next step that I can do. - Stop for me at two points only: the plan (step 4) and the full import (step 7). Find everything else yourself. The first answer (step 2) comes before both: it needs no question and sends no real customer data. - Send no real customer record, turn on no server and change no access in Harny before I approve the plan. - Never handle the value of an API key: do not print one, commit one, or ask me to paste one into this chat. The `harny` command saves the app key where only the command reads it. Never open, print or paste `.env`: check only whether a variable is in it. - Do not copy records that hold personal data into the chat. Describe them: the source, the count, the fields. - Write no API call from memory: read the API guide, https://api.harny.ai/llms.txt, and the commands below. - Every step starts with a check. When a step is done already, go to the next one. So I can paste the setup line again in a new chat: tell me what is done already (an app, users, data sent, Harny code), and continue from the first step that is not. ## How to ask me - When you have a tool that asks me questions with choices to click (for example AskUserQuestion in Claude Code), use it for every question in this setup. Write what I must read first as text before the tool call. - Put your guess first and mark it "(Recommended)". Give 2 to 4 choices, each a short label with one line of detail. Do not add an "Other" choice: I can always type my own answer. - Where I can pick more than one choice, mark each one that you guess. - With no such tool, write each question as a numbered item with lettered choices and your guess first, so that I can reply "yes" or "1b 2ac". ## 1. Check what you have Do not ask me. Find out: - **A Harny app**: if `.venv/bin/harny status` exists and shows `ok` on its `Key` line (it reads the saved key itself), a Harny app already exists for this project: do not create another. Go to step 3. - **A repository**: can you read my code and run commands in it? - **Harny code**: does the code already call Harny (`harny` in its dependencies, `HARNY_API_KEY` in its settings)? Continue from it. ## 2. Make the app, and get one answer There is no account to connect: one command makes the app. Put the two bootstrap files in one folder (they ship together): ```bash curl -fsSLO https://harny.ai/get/harny-init.py curl -fsSLO https://harny.ai/get/harny-0.1.1-py3-none-any.whl ``` Read the business name, what the app does and who its customers are from the project (README, package metadata). Do not ask the business what the project already answers, and never ask anyone for an email: the app needs none, and its owner gives their sign-in address when they claim it. Choose the model preset from what the project shows: `cost_first` (the lowest price per task), unless it shows health, finance, legal or children's data (`regulated`), or the business says answers must be the strongest (`quality_first`). The command stores it with the app, so Harny's own processing of what you load follows it from the first fact; the owner can change it after the claim. If the command says the preset was not stored (the app already existed), load only the first fact and say in your report that the rest load once the owner has stored it. Say why in your report. Give your own name too, with `--assistant`: the coding assistant you run as, in your own words. The owner sees it on their Home as who set the app up. If you do not know it, leave `--assistant` off; Harny never fills it in. Then run this one command with your values: ```bash python3 harny-init.py init --name "" --about "" --customers "" --preset --assistant "" --path developer ``` Choose the path before the first run. `--path developer` is for an app that stores facts and answers from them: it writes no setup folder and proposes no acting settings. For an app that acts for its users, pass one of the common setup paths instead (`--path P1` to `--path P5`; `harny init --help` names them): that run writes `harny-agent-setup/prompt.md`, which you then follow. To change the path later, run the command again with `--path `: a run without `--path` keeps the path the project already uses. It makes `.venv`, installs the Harny SDK there, creates the Harny app, saves the app key in `.env` (mode 600, ignored by git) without showing it, writes the settings that keep `.env` closed to Claude Code, Codex and Cursor, and prints the app id, a masked key and the claim link. It also adds a short Harny block to `AGENTS.md` and `CLAUDE.md` (made if missing; only the text between the `` and `` lines is ever added or replaced, so the rest of your file is untouched, and a second run changes nothing). The block tells the next coding agent that opens the project to use Harny for per-user memory and how to call it. Pass `--no-agent-files` to skip it. Keep the claim link for your report. If it stops, it prints one line that says what to do next. Do that, then run the same command again: it reuses the app. Now the first answer. It needs no model key and no real customer: use the command `.venv` now holds, `.venv/bin/harny` (on Windows `.venv\Scripts\harny`), a user named `test-user`, and one fact that the project's own files already state. ```bash .venv/bin/harny users add test-user .venv/bin/harny ingest --user test-user "" .venv/bin/harny query --user test-user "" ``` `query` prints the blocks Harny found; the fact you stored is in them. Show that in your report: it is Harny working. ### If you cannot run the bootstrap If `harny-init.py` cannot run (no shell, or no Python 3.10), create the app over HTTP. The answer carries the app key once, so for an agent that can only make HTTP calls the key passes through its context: that is one of two cases where an agent sees it (the MCP door below is the other), and why the command above is preferred. Write the request body with a JSON encoder, not by hand-quoting. `provision.json` holds `app_name`, `model_preset` (the preset you chose above), `objective`, `customers` and `assistant_name` (your own name; leave it out if you do not know it); it holds no `contact_email`, because the app needs none. ```bash curl -sS --fail-with-body -X POST https://api.harny.ai/v1/auth/provision -H "Content-Type: application/json" --data-binary @provision.json -o answer.json ``` `answer.json` holds `app_id`, `app_api_key`, `claim_url` and `claim_expires_at`. With a shell, move the key into `.env` without printing it, then delete both files, and make sure `.gitignore` lists `.env` and `answer.json` until you have: ```bash python3 -c 'import json,os;d=json.load(open("answer.json"));p=".env";t=open(p).read() if os.path.exists(p) else "";fd=os.open(p,os.O_WRONLY|os.O_APPEND|os.O_CREAT,0o600);os.fchmod(fd,0o600);os.write(fd,(("\n" if t and not t.endswith("\n") else "")+"HARNY_API_KEY=%s\nHARNY_APP_ID=%s\n"%(d["app_api_key"],d["app_id"])).encode());print("app id:",d["app_id"]);print("claim link:",d["claim_url"])' rm answer.json provision.json ``` Never put a key in code, chat or a commit. If the call answers 429, wait the seconds in `Retry-After` (when it sends none, try again tomorrow), then call again. The MCP server works the same way and has the same cost. Its address is https://api.harny.ai/v1/mcp/mcp (Streamable HTTP). Called with no Authorization header it offers only `provision_app`, which needs no key and whose answer carries the app key once. Replit's one-click install link for it is in https://harny.ai/llms.txt. ## 3. Look before you ask Read my repository, and ask me nothing yet. In a chat app with no repository, only do the last paragraph of this step. Find: - The app or the agent that gets memory, its language and framework, and where the model calls are made. - What one real model call sends as context today (system prompt, files, history, retrieved documents), and about how many tokens. That is the baseline. Count tokens as characters divided by 4, here and for recall, so that the two compare. - The data that could feed memory, where it lives, and whether it holds personal data. - Who reads what: the users or customer accounts, and the ids that name them. - Any memory that exists already (a vector database, embeddings, a transcript log), and any Harny code. Continue from it. Then read https://api.harny.ai/llms.txt for the calls that you will use. In a chat app with no repository, ask me to paste or attach a few records with my answers. ## 4. Show me the plan, and ask once Send me one message. It holds what you found and what is done already in five lines or fewer, the claim link, and the plan as a short checklist: - **The app**: what gets memory, and how: the Memory API called from my code, or the Harny MCP server for an agent that works with me. - **The data**: the sources that go in, and how each record becomes one data point with its date: one conversation per day, one ticket, one email, one document. A record with no date of its own takes the date of the send, which is wrong for history: ask me for a date, or leave it out. - **The split**: the ids of the users who read memory, one per person. Each user reads only what was sent for them. - **The recall**: where my app reads memory, and as which user. - **The test**: the 5 to 20 records that you send first, the real questions that you recall with, and what the test adds to my monthly active users. - **The key**: where the model key goes when my app needs written answers: `.env` as `HARNY_MODEL_API_KEY`, set by me, never in chat. Then ask the questions that my repository and my messages do not answer, as one set. Use the bold word as the header of each question, and mark a guess on each: 1. **Building**: which app or agent gets memory? Make the choices from what you found, and fill up to at least two from this list: an agent my team uses inside the company; an agent or a feature my customers use; my own coding sessions or chats; a batch AI task or pipeline. 2. **Data**: which sources go in first? Make the choices from what you found, and let me pick more than one. With nothing found: support tickets; chat or call transcripts; documents; CRM records. In the detail of each choice, say whether it holds personal data. 3. **Access**: how is memory split, and who reads which part? Choices: one user per end user, who reads only their own; one user per customer account or team. 4. **Recall**: where does my app use memory? Choices: in the chatbot, before each answer; as a tool that the agent calls when it needs to; in a proactive or scheduled agent that acts on what it recalls. **Wait for my answer.** My answers approve the plan. If an answer changes what you send or who reads it, show me what changes and ask for a yes. With no question left, ask one: approve the plan, or change it. ## 5. Set it up 1. The users of the plan: `.venv/bin/harny users add ` for each reader. 2. The key for my code: check that `HARNY_API_KEY` is set, without printing it, and that `.gitignore` covers `.env`. Add the line to `.gitignore` if it does not. 3. Check it: `.venv/bin/harny status`, and `.venv/bin/harny query --user "test"` for one user of the plan returns nothing yet, with no error. ## 6. Test with a little data 1. Send the test records, each with its date, from a short script that reads `HARNY_API_KEY`: `POST /v1/memory/ingest` for one data point (`content`, and optionally `id`, `label`, `timestamp`) or `POST /v1/memory/ingest/batch` for up to 1,000, each call with `X-Harny-User: `. With the SDK installed, `.venv/bin/harny memory ingest --user --timestamp --wait ""` sends one data point with its date, and `.venv/bin/harny memory batch --user --wait ` sends up to 1,000 from a file (one JSON object per line, or a JSON array). `harny ingest --user ""` stores one fact with no date of its own: use it for a smoke test, not for history. In your own code use the SDK's `ingest_data_point` and `ingest_batch`. 2. Ingestion is asynchronous: a recall before it completes finds nothing. Poll `GET /v1/memory/ingest/{ingestionId}` until `status` is `completed` (or run `.venv/bin/harny memory status --wait `). 3. Recall the questions of the plan, as the users that will ask them: `POST /v1/memory/recall` with `query`, `identity` and `level` (or `.venv/bin/harny memory recall --user ""`). 4. Check that it works well, and show me the result as a short table: the right memories come back, with their sources and dates; the same question, asked as a user whose data it is not, does not return it; what recall returns, beside what the baseline call sends today, with the token counts. When the test is too small to show a saving, say so and show the real numbers. 5. When a check fails, change the cut, the dates or the users, and test again. Do not go on with a failed check. 6. Count the full import: the records, the bytes, and `GET /v1/memory/usage` (or `.venv/bin/harny memory usage`) for what the app has used this month. Show me both. 7. Ask me for a yes to the full import, and to the data that arrives from now on. **Wait for my answer.** ## 7. Send the rest 1. Send the history in batches of up to 1,000 data points with an `idempotencyKey` per batch, and wait until each batch is completed. A batch that stops resumes when you send it again with the same key. 2. Wire the send path into my code: each new or changed record goes to Harny with its `id`. The same `id` with changed content replaces the old memory. ## 8. Put recall where my app needs it Wire recall where the plan says. Prove it with a test or a short script that recalls as a real user, with no paid model call, and show me the result: - **A chatbot**: before each answer, recall with the message as the query and the user as `identity`, and give the result to the model in place of the long history. - **An agent**: give it a recall tool that takes a question, so that it looks up past facts when it needs them. - **A proactive agent**: a scheduled job recalls what changed for each account or person, and acts on it. ## Where keys go | Key | Where it goes | Who puts it there | |---|---|---| | The app key (`fsa_...`) | `.env`, written by `harny init` | `harny init`. No one types it and no command prints it. | | The business's own model key | `.env` as `HARNY_MODEL_API_KEY` with `HARNY_MODEL_PROVIDER`, or the provider's own variable such as `ANTHROPIC_API_KEY` | The owner, after the first answer works. Never in chat. | | A secret for signing your app's sign-in tokens | Made by `.venv/bin/harny env generate STARTER_SESSION_SECRET` (saved in `.env`, never shown) | The command. | On Replit: `.env` works in the workspace. Replit writes Secrets only in its Secrets tool (Tools, then Secrets) and documents no command that writes them, so the owner adds the model key there; Replit syncs Secrets to a published app. The Harny app key and the sign-in secret stay in `.env`; if the published app does not find it, the owner puts them into Secrets once. ## Claim Give the claim link to the business owner. Opening it signs them in to Harny and makes them the app's owner. You never complete a claim. The link expires; run `.venv/bin/harny init` again to print a fresh one for the same app. Owner: you can ignore everything above. Open the link your assistant gave you, sign in, and the app is yours. ## When you are done Tell me in three lines what works now, and give me the claim link. For a Replit app, start from the Replit starter: https://harny.ai/get/replit-starter.zip. Then offer me the one next step that fits best. ## When something fails - A `/v1/memory` call or a `harny memory recall` command that answers 404: the data-point routes are not switched on for this deployment. Use `harny ingest --user ""` and `harny query --user ""` instead: they store and read the same memory, one fact at a time and without dates. Tell the owner. - A send that does not complete: `GET /v1/memory/ingest/{ingestionId}` shows `failed` and why. Do not retry in a loop. - An error code: the API's own guide, https://api.harny.ai/llms.txt, explains each one. The error line from a `harny` command names the code. - A new key or claim link goes to me and nowhere else. --- # Security for enterprise review Source: https://harny.ai/security Harny keeps each app's data apart, checks every request against the app that sent it, and records every action an agent takes. Written answers to a security questionnaire and a summary of the security measures are available on request. ## Reviews and agreements - **Controls by SOC 2 category.** Harny lists its security controls under the five category names of SOC 2's Trust Services Criteria: Security (one sign-in layer, API keys stored as one-way hashes and revocable at any time, every read scoped to the calling app, and the app's acting settings enforced on Harny's servers), Availability (rate limits and daily quotas that keep counting when a supporting service is down), Processing Integrity (a numbered, hash-chained action record you export and verify, and one error format on every route), Confidentiality (credentials encrypted before storage, credential-like strings removed before a model sees content, and fetched source copies deleted on a schedule) and Privacy (delete any stored item with the memories that rest only on it, and a retention schedule for children's data). - **Data agreement and roles.** The business and Harny are joint controllers for collecting users' data through the business's app, and the agreement says who answers each request from a user. - **Written security answers.** Written security answers and a security questionnaire are available on request through support@harny.ai with Security in the subject. ## How Harny handles your data - **Encryption in transit and for credentials.** Connections use TLS. The tokens users grant for connected accounts, and the secrets that sign webhooks to you, are encrypted at rest in the application. The encryption keys can be rotated. - **Source retention and deletion.** One erasure path removes a user's data across every store that holds it, and an automated test checks that it is complete. Erasure finishes within 30 days. You can delete a single stored item. A deleted record keeps its numbered place with no content. - **Apps are isolated.** Every read is scoped to the calling app. A request for another app's or another user's item returns "not found". Stored source content never crosses from one app to another. - **Before a model sees content.** Strings that look like credentials are removed before content goes to a model provider, and lines in email content that try to instruct the agent are removed. - **Restricted production access.** People at Harny do not read the content of users' connected accounts except with the user's consent, for security or where the law requires it, and production access is limited to a few named engineers with multi-factor sign-in and logging. ## Identity and access - **API key revocation.** The business can revoke an API key at any time. Keys are shown once, when created, and stored only as one-way hashes. - **One sign-in layer.** Every request is resolved through a single authentication layer. - **Actions stay inside the owner's settings.** The app's acting settings are enforced on Harny's servers, not in the agent. An action that needs a confirmation never runs without it, and a confirmation runs the action once, exactly as confirmed. Actions are rate limited per user and per kind of action. ## Operations - **Where it runs.** Data stored in the US. Model providers follow the app's model setting. - **Traces and tooling.** Operational traces are cleaned of content before they are stored. Administrative tools are deployed separately from the customer-facing service, and a build check keeps customer-facing code from importing them. The host exposes only web traffic and administrative access, behind an edge network. - **Incident notice.** Harny tells the business without undue delay after it becomes aware of a security incident affecting the business's users. The notice says what Harny knows: what happened, how many users and records, the likely consequences, the measures taken and a contact. Harny makes the regulator notices the law requires of it. ## Frequently asked questions ### How does Harny handle data-protection law? The business and Harny sign a data agreement. It sets who is responsible for what, who answers each kind of request from a user, and how an incident is reported. For users the business flags as children, Harny works only for the business. ### Does Harny use my data to train AI models? No. Harny does not use your data to train general AI models. That covers data from a Google account and everything else you store with Harny. ### How is data deleted? Delete a single stored item, or ask for a user's data to be erased. One erasure path removes the data across every store, and it finishes within 30 days. ### Can I audit what the agent did? Yes. Every action is written to a numbered record; each entry carries a hash of the one before. Export it as JSON or CSV and verify that nothing is missing or altered. A record removed by an erasure is counted, not hidden. ### Can I leave? Yes. Delete any data point, erase any person across every store, export the action record, and export a person's memory. ### Where does Harny run? Data stored in the US. Model providers follow the app's model setting. ### How do I get a security questionnaire answered, or the data agreement? Write to support@harny.ai with Security in the subject. Harny gives written answers to a reasonable security questionnaire (once in any 12 months) and a summary of its security measures. The data agreement is available on request. ### How do I report a vulnerability? Write to support@harny.ai. Security reports are read first. ## Detail for agents and reviewers The next blocks are the text of Harny's data agreement, unchanged, so a reviewer can check each statement above against the measure that backs it. "The Business" is the company that builds the app. "End User" is a person who uses the app. "Section" numbers refer to the data agreement. ### Who is responsible for what | Processing | The Business | Harny | |---|---|---| | Joint Service | Joint controller. [contact details] | Joint controller. support@harny.ai | | Harny's Own Purposes | Not a party to the purposes | Controller | | Child-Flagged Users | Controller; operator under COPPA | Processor and service provider | ### Confidentiality and human access 6.1 Harny ensures that everyone it authorises to process Personal Data is bound by a duty of confidentiality. 6.2 Harny's people do not read the content of End Users' Connected Accounts except in three cases: - (a) with the End User's affirmative consent for specific items; - (b) where it is necessary for security, such as investigating abuse; - (c) where it is necessary to comply with the law. ### Technical and organisational measures in place | Area | Measure | |---|---| | Encryption in transit | Connections to the Service use TLS | | Encryption of credentials | The tokens End Users grant for Connected Accounts, and the secrets used to sign webhooks, are encrypted at rest in the application. The encryption keys can be rotated | | API keys | Stored only as one-way hashes. Shown once when created. Revocable at any time by the Business | | One authentication layer | Every request resolves who is calling through a single authentication layer. New routes cannot parse credentials on their own | | Tenant isolation | Every read is scoped to the calling app. A request for another app's or another End User's item returns "not found" | | Cross-app boundary | Stored source content never crosses from one app to another | | Secret removal before model calls | Credential-shaped and secret-shaped strings are removed from content before it is sent to a model provider | | Hardening of incoming content | Content read from an email before it reaches an agent is cleaned. Lines that try to instruct an agent are removed and flagged | | Action safeguards | The Acting Policy is enforced on Harny's servers, not in the agent. See the list below | | Tamper-evident record | Each record is sealed into a per-app numbered sequence in which every entry carries a hash of the one before. Exports can verify that nothing is missing or altered | | Signed outbound events | Webhooks are signed (HMAC-SHA256) and carry a stable event identifier across retries. Notices carry a cleaned subject-line summary and the sender domain, never the message body | | Rate limits and quotas | Limits apply per End User and per app on content intake and on Actions | | Separation of operator tools | Administrative tools are deployed separately from the customer-facing Service. A build check keeps customer-facing code from importing them | | Network exposure | The application host exposes only web traffic and administrative access, behind an edge network | | Erasure | One erasure path removes an End User's data across every store that holds it. It is checked by an automated completeness test | | Human access | Limited as Section 6.2 describes | | Observability | Operational traces are cleaned of content before they are stored | | Child-Flagged Users | Own-Purpose uses switched off, and the Business's retention limit, or the default in V.5, enforced across every store | **Action safeguards in detail:** - Harny works out the kinds of action of each request itself, from what the Action does, and the request follows the most careful of the Business's choices for the kinds it is. After the agent may have read someone else's content, an invitation or message to an address the End User never gave waits for the End User's yes. - A confirmation reference is never passed to the agent's model. - A confirmation runs the confirmed Action once, exactly as confirmed. - An Action that needs confirmation never runs without it. - Actions are rate-limited per End User and per kind of action. - Disconnecting an account stops reading and acting on it immediately, and revokes the token at the provider. **What this annex does not claim.** - Harny holds no third-party security certification. - Customers cannot manage their own encryption keys. - Encryption at rest of the database and search stores is provided by the hosting providers and is not represented here. ### Retention | Material | How long it is kept | |---|---| | Copies of items fetched from a Connected Account | A set period per connection, 90 days by default. The Business may shorten or lengthen it, or set no expiry | | Derived information | No expiry. Kept until erased, or until the specific item is deleted | | The Record | No time limit. Deleted when the End User's data is erased, or when the Business asks Harny to delete the app. A deleted record keeps its numbered place in the sequence, with no content and no End User | | Requests waiting for confirmation | Expire after 15 minutes. The outcome is recorded | | Execution details of each attempt | 30 days | | Notices of newly arrived items | 14 days | | Events waiting to be delivered | 30 days | | Other operational material (delivery attempts, query traces) | Shorter periods, on rolling schedules | | Sender and category counts | Until the End User's data is erased | | Sharing choices | As long as the choice stands, then `[period]` as evidence of the choice | | Aggregated statistics describing no individual | May be kept longer | | Everything held about a Child-Flagged User | No longer than the retention limit the Business sets, or the default in V.5 where it sets none (Annex V) | Erasure is carried out without undue delay and completes within 30 days. The Joint Service lasts as long as the Terms. ### Security incidents 10.1 Harny notifies the Business without undue delay after becoming aware of a Security Incident affecting Personal Data from the Business's app. 10.2 The notice describes, as far as Harny knows at the time: - the nature of the incident; - the categories and approximate number of data subjects and records affected; - the likely consequences; - the measures taken or proposed; - a contact point. Harny may provide the information in stages as it becomes known. 10.3 **Who notifies whom.** For a Security Incident in Harny's systems, Harny makes the notifications to supervisory authorities that Data Protection Law requires of it. Where End Users must be told, Harny tells them through the Business's app, or directly where it holds their contact details. The Business does the same for incidents in its own systems. The parties coordinate the content and timing of any notice to End Users. For Child-Flagged Users, Annex V applies instead. 10.4 A notice under this Section is not an admission of fault or liability. ### Where data is held As on the security page: Data stored in the US. Model providers follow the app's model setting. --- # Harny, run where you can see every call Source: https://harny.ai/enterprise Harny runs as a managed service. You call the API and nothing else changes. Each app keeps its own data, and every action an agent takes is written to a record you can export. ## Choose how Harny is deployed ### Managed cloud We run the service. You call the API and nothing else changes. - Data: Harny's infrastructure - Compute: Harny's infrastructure - Upgrades: Automatic - Every read is scoped to the calling app - Encrypted in transit; users' connected-account tokens encrypted at rest - Console for the app, its keys, its users and the record ## Available for review A summary of the security measures and a data agreement that sets the roles under data-protection law are available on request. Written security answers and a security questionnaire are available on request through support@harny.ai with Security in the subject. ## Governed at the data layer The controls sit in the service rather than in a wrapper around it, so they apply to every call. - **Separated per app and per user.** Every read is scoped to the calling app. A request for another app's or another user's item returns "not found". Stored source content never crosses from one app to another. - **Retention you set.** You set how long copies of items fetched from a connected account are kept: 90 days by default, shorter, longer, or no expiry. Derived information is kept until it is erased or the item is deleted. Erasure finishes within 30 days. - **Every action on the record.** Every action an agent takes for a user is written to a numbered record for the app. Export it as JSON or CSV for the app or for one user, with a check that nothing is missing or altered. ## Frequently asked questions ### Need Harny in your own environment? Talk to us. Contact: support@harny.ai ## Talk to the team Send us your deployment constraints, your expected volume and your review process. We will tell you what fits and what does not. Contact: support@harny.ai --- # Changelog Source: https://harny.ai/changelog What changed on harny.ai and in the Harny service, newest first. - 2026-10-27 (Agents): **Long jobs: agents that finish whole office jobs.** A Harny agent can now take a whole job, such as 150 emails, 120 requests or 25 leases, work through it in your own folder for up to an hour a session, and return the finished files. See the Long jobs page. - 2026-10-27 (Developers): **Files that let a coding agent set up Harny.** harny.ai now serves openapi.json, mcp.json, an API catalog, a quickstart in Markdown, llms.txt and an agent setup prompt. A coding agent can read how to set Harny up without a human. - 2026-10-26 (Developers): **Self-serve sign-up.** Get API key now goes to sign-up. You create an account, get your first app key on the first screen, and no waiting list stands between you and a key. - 2026-10-26 (Site): **Explainer pages on agent memory.** The site now explains agent memory in plain words, with guides and a glossary on terms such as knowledge updates and as-of recall. - 2026-10-25 (Console): **Hand an app to another account.** An app owner can give the app to another account with a hand-over link. A different Google account that connects replaces the old connection and sees a notice. The sharing question is asked again. - 2026-10-24 (Site): **A separate release for harny.ai.** The landing page, the benchmarks page, the legal pages and the early-access form now ship as one static release. - 2026-10-18 (Plans): **New apps start on the Free plan.** New apps start on the Free plan with one app live at a time. Other apps pause, and their keys, settings and users' data stay kept. Putting a learned change back is always free. - 2026-10-13 (Console): **Learned changes, in one tap.** When users keep accepting the same thing, the console tells the owner what the users did, then offers the change in one tap. A change can always be put back. - 2026-10-12 (Console): **Manual actions: the steps only a person can take.** A manual actions page lists the steps only a person can take, one line per kind with a count and a Done button. You choose how strict the rule is: Strict, Strict with a daily note, or Looser. You can also get a daily email. - 2026-10-11 (Console): **The Changes log.** The owner sees one list of everything that changed in an app: settings, keys and connections. It runs newest first and filters by kind and by who made the change. - 2026-10-09 (Memory): **Situation awareness for conflicting memories.** Each app gets a control with three modes, off, advisory and applied, that checks what a memory says against what else is known. A second setting decides whether the agent asks the user about a contradiction. - 2026-10-07 (Developers): **Sign in to the MCP server.** An assistant can connect to the Harny MCP server with a sign-in. A person gets one app, the end user's key never reaches the agent, and desktop apps get proper return addresses. - 2026-09-25 (Console): **The Actions section.** The console has an Actions section. Activity shows what the agent did for each user, as done, waiting, refused, undone or failed, and downloads as CSV or JSON. Settings sets each kind of action to act, ask the user first, or off. - 2026-09-25 (Developers): **A coding assistant sets up an agent from one prompt.** A coding assistant can now stand up a business's agent from Harny's setup prompt alone, with no human help. - 2026-09-22 (Agents): **Agent kit: loop, presets and a starter app.** The agent kit gives you an agent loop with presets that run on your own model key, a spend cap per run, and hooks before and after an action. A starter app brings a chat page, a webhook, scheduled runs and deploy recipes. - 2026-09-22 (Memory): **A new email can wake your agent.** A new email on a connected source can wake your agent through a webhook, so it can act on mail as it arrives. - 2026-09-08 (Benchmarks): **The benchmarks page.** Harny's published results are on one page, with the tier, the answering model, the judge and the scoring rule stated beside each figure. --- # Harny blog Source: https://harny.ai/blog The Harny blog: posts from the Harny team on research, engineering and announcements, for developers building AI agents and for the AI assistants that read this site. The RSS feed lists new posts in a feed reader. Categories: Research, Engineering, Announcements. Feed: https://harny.ai/blog/rss.xml No posts yet. --- --- # Terms of service Source: https://harny.ai/terms The terms for the Harny website, the API, the console and the connectors. ## The parties ### 1. Definitions In these Terms: **"Harny"**, **"we"**, **"us"** means Vexta Labs Inc., a company incorporated in British Columbia, Canada, of Burnaby, British Columbia. **"Developer"**, **"you"** means the person or company that creates an App on the Harny platform, whether or not any fee is payable. **"App"** means an application you register with Harny. Each App has its own credentials, its own End Users, and its own settings. **"End User"** means an individual who uses your App and whose data reaches Harny through your App. End Users are your users. Your relationship with them is governed by your own terms and privacy policy. How Harny handles their data is also described in Harny's privacy notice and in the End-User Notice. **"Child-Flagged User"** means an End User you have marked to us as a child under Section 4.10. **"End-User Notice"** means Harny's own short notice to End Users, which we publish and which you show at connect time (Section 4.2). **"Connected Source"** means a third-party account — for example an email mailbox or a calendar — that an End User authorises your App to read through Harny. **"Context"** means the information Harny derives from Connected Sources and from content your App sends us, together with the material we retain so it can be searched and retrieved. Context is what Harny returns to your App so your agent can answer well. **"Record"** means the account Harny keeps of what your agent did — the actions it reported, their outcomes, and the material Harny supplied beforehand. The Record is evidence about your agent's behaviour. It is not an instruction to your agent and it is not Harny acting. **"Run record"** means the list of steps your agent took in one run, as your agent's code reports it to us. It is not part of the Record. **"Service"** means the Harny platform: the API, the developer console, the SDKs, and the connectors, taken together. ### 2. What the Service does — and what it does not do #### 2.1 What we supply Harny is infrastructure that sits underneath your App. Across a single chain, we help you set the App up, connect Sources, let your End Users authorise those Sources, assemble the right Context for a given moment, and keep the Record of what your agent did afterwards, so you can watch, debug, and evaluate it. #### 2.2 What Harny does, and what your agent does **Harny supplies Context, carries out the Actions your agent instructs within the End User's permission and the rules you set, and keeps the Record.** Your agent decides. It runs in your systems, under your control, using your own model and your own tools. When your agent decides something, Harny's role is to have supplied Context before it, to carry out an Action only if you enabled it and the End User allowed it, and to record what your agent reported afterwards. Where you enable Actions for your app, Harny also carries out the steps your agent instructs on an End User's Connected Sources — inside what that End User allowed. Three rules apply, and all three are deliberate: 1. **Reading and acting are separate permissions.** Every Source connector reads with read-only permissions the End User granted for reading. Acting on a Source (sending a message, creating an event, writing a page) needs a separate write permission the End User grants, and an Action you enabled for your app. Forwarded email we receive; we send email as an End User only under an enabled Action and a granted write permission. 2. **You decide what your agent may do, and how it asks.** For each app you choose the acting policy: which kinds of Actions run without asking, which ask the End User first, and which are never allowed. In test mode, the settings your setup assistant proposed apply until you change them, and changes save at once. When you switch the app to production you confirm them; after that a change needs your confirmation. A kind of action with no setting is not carried out: the agent reads and records only. Your agent's instructions are yours; carrying them out within the permission and the policy is ours. 3. **Every Action is on the record.** What was instructed, by which agent, for which End User, under which permission, what happened, and what failed — kept in the action record you and the End User can read, and revocable by the End User at any time, which stops further Actions on that Source immediately. ## Early access #### 2.3 The Service changes We add, change, and remove capabilities as the platform develops. We give notice of changes that materially reduce what the Service does for you, on the notice terms in Section 12. ### 12. Changes to these Terms We may change these Terms on at least 30 days' notice, given by email to your developer account and in the console. Continuing to use the Service after the notice period means you accept the change. If you do not accept a change, terminate under Section 11.2 before it takes effect. Changes required by law or needed to address a security risk may take effect immediately, and we will tell you as soon as we can. ## Accounts and API keys ### 3. Your account, your Apps, and your keys #### 3.1 Account You must give accurate registration details and keep them current. You are responsible for everything done under your account. #### 3.2 Credentials Your App authenticates to Harny with credentials you create in the console. You can list, create, and revoke API keys and client ingest keys yourself through the console and the API. You must keep credentials secret, must not embed a server-side key in client software you distribute, and must revoke a key promptly if you believe it has been exposed. If you think a key has been compromised, tell us at support@harny.ai. #### 3.3 Access to Google Sources is granted, not automatic Connections to Gmail, Google Calendar, and Google Drive are **not available to every App by default.** Access to Google Sources is subject to Google's own verification requirements and limits. Connections to these Sources require approval by Harny, which may be refused. **Nothing in these Terms entitles you to a Google connection.** Approval is at our discretion and depends on capacity we do not control. ### 9. Your obligations to us You are responsible for: - having the right to send us the data you send us; - your own disclosures and consents to your End Users (Section 4.2); - showing what Section 4.2 asks you to show at connect time, unchanged; - obtaining verifiable parental consent for child End Users, and flagging them (Section 4.10); - what your agent does with the Context we supply; - keeping your credentials secret; - the acts of anyone using your account. ## Acceptable use ### 6. Acceptable use You must not, and must not permit anyone to: - use the Service to build or operate a surveillance product; - resell standalone access to the Service as if it were your own infrastructure. Building and operating an App on behalf of a client, as an agency or under a white label, is permitted; - pass Context or Records to an advertising network, data broker, or any third party for marketing purposes; - probe, load-test, or attempt to circumvent quotas, rate limits, or access gates, including the gate in Section 3.3; - reverse-engineer the Service except to the extent that restriction is unenforceable where you are; - use the Service where doing so is illegal, or in breach of a Connected Source's terms. We may suspend an App that is doing any of the above. Section 11 governs suspension. ## Your data ### 4. End User data and consent #### 4.1 Who holds the relationship with the End User Your End Users are your users, and you hold the relationship with them. You and we are jointly responsible for collecting their data through your App and for processing it to deliver the Service to your App. We are responsible on our own for the purposes we decide alone (Section 13). You are responsible for having the right to send us their data and for telling them, in your own terms or privacy policy, that you do. #### 4.2 What you must tell your End Users Before an End User connects a Source, you must disclose to them, in substance: - that a third-party infrastructure service processes context from the account they are about to connect; - that, with their consent, the service also uses what it learns about their preferences and habits to personalise the other apps they use with the same service; - what that service keeps and for how long (Section 4.3 and Section 4.4); - that they can disconnect at any time, and how to ask for deletion. **The disclosure clause.** A clause covering these points is provided in your console on the App settings page. Include it, or your own wording covering the same points. **Our End-User Notice.** At connect time, before the End User authorises a Source, you must also show: - the short form of our End-User Notice, with its link, as we supply it; - where your App takes part, our question in Section 4.9, unchanged, separate from your own terms, and not pre-selected. You pass the End User's answer to us. Using the template does not transfer responsibility: making your own disclosure accurate and lawful where your End Users live is yours. Our End-User Notice is ours to keep accurate. #### 4.3 What Harny stores Harny stores: 1. structured facts drawn from the content; 2. short summaries of what a piece of content meant; 3. representations of that material used to search it; 4. metadata anchors — identifiers, timestamps, sender domain, size, category; 5. copies of items fetched from a Connected Source, kept so we can serve and re-process them without going back to the Source each time; 6. an audit trail of the processing itself; 7. the Record of actions your agent reported; 8. the Run record of your agent's work: when each run started and ended, its outcome and stop reason, step counts and token counts, the model preset used, an optional session reference you choose, and a short summary of each step. **Source copies include the content of the item.** An item fetched from a Connected Source is stored as a copy that holds, depending on the Source, either the item's text as received, including its subject line where it has one, or a set of fields extracted from the item. **Short content is stored as written.** Where a piece of content is short enough that the whole of it is the fact, we store it as it came in rather than deriving a separate summary from it. Longer content is decomposed into separate facts. **Run records hold summaries, not content.** For each run, we store its timing, outcome, stop reason, counts, model preset and the agent instruction version used. For each step, we store the kind of step, the tool name, whether it succeeded, was refused, failed or was cancelled, how long it took, a short summary of its input and output, and a link to the Record entry it produced. Summaries are a few lines long, with credentials and secrets removed. They hold no End User message text, no text of agent answers, no file contents and no bulk tool output; only the length of an answer is kept. Run records are kept apart from Context, are not used to derive Context, and are not shared with other Apps. We also read your Run records to show you where your agent keeps failing and to suggest a change to your agent's instructions; a suggestion is saved as a new version and changes nothing until you or your coding assistant makes it current. **Two boundaries apply to stored source content and do not change:** - **It does not cross the App boundary.** Stored source content, including content stored as written, never goes to another App. - **It is not returned as source content.** Source content is not returned as such through the developer interface. Within your own App, the Context we return may quote a short excerpt of the material it was drawn from. #### 4.4 How long we keep it Different material is kept for different periods: - **Items fetched from a Connected Source** are kept for a set period, configurable per connection, defaulting to 90 days, after which they are deleted automatically. The period can be shortened, lengthened, or set to no expiry. - **Derived Context** — facts, summaries, and search representations — **has no expiry. It exists until it is deleted.** No timer and no sweep removes it. It goes when the End User's data is erased, or when a specific item is deleted. - **Records of agent actions** have no time limit. A Record about an End User goes when that End User's data is erased; every Record of your App goes when you ask us to delete the App. - **Run records** are kept for 30 days from the run, then deleted automatically. They are also erased with the End User's data and when the App is deleted. - **Operational material** — execution logs, delivery attempts, and query traces — is swept on shorter schedules. #### 4.5 Third-party processing Content is processed by third-party model providers in order to derive Context, under our agreements with those providers. **Where data is processed.** We publish where the Service and each of our provider categories process data, and update it when a location changes. Today: | What | Where | |---|---| | Application server | United States | | Main database | United States | | Model providers that derive Context | People's Republic of China first, United States as the last fallback | | Model providers that build search representations | United States | The full list, including email delivery, the edge network, the tool-integration platform that carries Actions on your own tools, sign-in and console hosting, is Annex III of the data-sharing agreement. **The regulated route.** If your App cannot send End Users' data to China-hosted providers, select the "regulated" model preset. Under it, both your agent's model and our own processing of your App's content use only providers tagged for regulated workloads, hosted in the United States. **Where data is stored.** Data is stored in the United States. #### 4.6 End User rights Harny supports the following. The honest position on each is stated, because a right described more grandly than it works is worse than one described plainly. **Deletion.** An End User's data can be erased. A signed-in user can erase their own data; you can initiate erasure for one of your End Users through the API; and progress can be checked through the erasure-status endpoint. Erasure removes derived Context from the memory stores, the copies fetched from Connected Sources, Records and Run records attributed to that End User, and the other material held about them. We act on an erasure request without undue delay and complete within 30 days. These are kept: - what the law requires us to keep; - the record we hold for the End User survives in scrubbed form, with name and email removed, until your App is deleted; - a minimal audit record that the erasure happened survives, in a form that does not identify the End User; - the record of a request the End User made to us — who asked, what they asked and when we answered — kept to show we handled it, no longer linked to the End User's data; - daily usage counts, a day and a number with no content, kept for billing; - Records that are scoped to your App rather than to a person — maintenance jobs — are not attributed to an End User and are kept until you ask us to delete the App, rather than removed by a per-End-User erasure; - aggregated statistics describing no individual; - copies in backups, until the backups are overwritten. They are not used for anything until then. **Export and access.** An End User may ask for a copy of the Context we hold about them, and we will provide it in a machine-readable form. This is a request path: you or your End User asks us, and we produce it. You can also export a person's stored data points and memories yourself through the API's export route. **Rectification, restriction, objection.** Handled through your App — re-authorising a connection refreshes the data; disconnecting stops further processing — or by contacting us. **We answer End Users directly.** For data held in our systems, an End User may come to us instead of you, and we answer them ourselves. That includes objecting to our use of their data to improve the Service, and withdrawing a consent they gave us, by emailing support@harny.ai. We tell you when a request affects your App. You forward to us, without undue delay, requests you receive about data only we hold. #### 4.7 Deletion when a connection ends When you or your End User disconnects a Source, we stop retrieving from it and stop acting on it. The copies fetched from it are deleted at once if you ask for that when you disconnect; otherwise they expire on the connection's period in Section 4.4. Derived Context stays until it is erased or the item is deleted (Section 4.4). Disconnecting never changes anything in the Source itself. #### 4.8 Special categories, children, surveillance You must not use the Service to: - process data about a person without their knowledge, for the purpose of monitoring them; - deliberately ingest special-category data — health, religion, sexual orientation, political opinion, biometrics — unless your End User has given explicit consent for exactly that; - offer the Service to a child End User — anyone under 13, or under the age of digital consent where they live, whichever is higher — unless you have obtained verifiable parental consent, or the consent the law of the child's location requires, and have flagged that End User to us as a child (Section 4.10). Content about a child that reaches us through an adult End User's own Connected Account, such as a message from a school to a parent, is permitted where the processing serves that adult; - ingest data in breach of a Connected Source's own terms. #### 4.9 Personalisation across Apps With an End User's consent, we use what we learn about their preferences and habits to personalise the other Apps they use with Harny. We ask the End User for that consent in our own name at connect time, and they may withdraw it at any time. Your App takes part unless you switch this off, which you may do at any time; switching it off stops such information being drawn from or served to your App. An App receives such information only about End Users it already has, and never stored source content (Section 4.3). An End User's consent, and the information it covers, remain under that End User's control if your App later stops using the Service. #### 4.10 Children as End Users Children may be End Users of your App when you have obtained verifiable parental consent, or the consent the law of the child's location requires. For them: - **You flag them.** You mark every child End User to us, through the API, before any of the child's accounts is connected. We do not detect or infer age. - **You may set a retention limit.** If you set how long we keep children's data, we delete each item held about a Child-Flagged User, including their Records, once it is older than that limit. If you set none, we keep a Child-Flagged User's data while the child uses your App, and delete it after 12 months in which the child did not use it. Either way, we delete it within 30 days after a parent asks, you ask, or you remove the child or close the child's account. We keep only daily usage counts, with no content, for billing. - **We act only as your service provider.** For a Child-Flagged User we process data only to provide the Service to your App. None of our own purposes in Section 13 apply to their data, and we produce no statistics from it other than counts. - **Our written assurances.** We give you the written assurances on confidentiality, security, integrity and retention that children's privacy law requires of service providers. They are in the data-sharing agreement. - **If we learn of an unflagged child,** we tell you and treat that End User as flagged from then on. If you do not promptly confirm verifiable parental consent, we delete that End User's data. ### 5. Consumption-tendency signals The following disclosure is reproduced word for word: > "Harny counts which senders and categories write to a connected inbox (never the email content) to power personalization; this is stored separately from the user's memory." To put that in context for a developer reading these Terms: the count is metadata only — sender domain, category, and date. No message body and no subject line is kept. It is held in a separate store from the End User's Context and is never used to answer queries, so it cannot surface as context in an answer. Senders in sensitive categories, such as financial, medical, legal, and insurance, are excluded. ### 13. Data protection roles **We are responsible for the data we handle, in our own right.** Three roles apply: 1. **Together, for the Service.** You and we are joint controllers for collecting End Users' data through your App and processing it to deliver the Service to your App. You decide which Sources your End Users may connect, what your agent asks for, your acting policy, retention on each connection and your setting in Section 4.9. We decide how the Service collects, stores, derives, secures and records data. You give your own notice and show ours; we answer End Users directly for data in our systems. 2. **On our own, for our purposes.** We are the controller for the purposes we decide alone, each on its own lawful basis: personalisation across Apps under Section 4.9 (the End User's consent); security of the Service, improving it, and aggregated statistics describing no individual (legitimate interests, with safeguards); sender-and-category counts (legitimate interests); billing (these Terms). Our privacy notice describes them. 3. **For you only, for children.** For Child-Flagged Users we are your processor and service provider only, and none of our own purposes apply (Section 4.10). **The rights you grant us.** You grant us a non-exclusive, royalty-free, worldwide licence to use content processed through the platform to operate and improve the Service, and to produce aggregated cohort statistics describing no individual, in each case except for Child-Flagged Users' content. The licence over aggregated cohort statistics survives termination. **The data-sharing agreement.** Our Data Sharing and Joint-Controller Agreement sets out these roles in detail, including the transfer clauses and the terms for Child-Flagged Users. It forms part of these Terms. ## Fees ### 7. Pricing and tiers #### 7.1 What you pay for **What you pay for is the number of End Users who used your App in the calendar month.** You can create any number of Apps. One App is active and free at a time. In it, the first 500 monthly-active End Users are free, forever. For each monthly-active End User above 500 in an App, you pay $0.30 per month. Your current count is shown in your console. #### 7.2 We can change the pricing **We may change our tiers, our thresholds, and how we charge, at any time, on at least 30 days' notice to you.** We give notice by email to the address on your developer account and in the console. If you do not accept a change, your remedy is to stop using the Service before the change takes effect, and to terminate under Section 11. Continuing to use the Service after the notice period means you accept the change. #### 7.3 Paid periods are honoured **A change to pricing does not disturb a period you have already paid for.** If you have paid for a term, the terms you paid under run to the end of that term, and the change applies from your next renewal. #### 7.4 Free tier The free tier is free. We may change what it includes on the notice in Section 7.2. We do not undertake to keep any particular capability free. #### 7.5 Taxes and payment Fees are exclusive of taxes, which you pay in addition where they apply. Fees are payable in the currency stated on the pricing page. Fees already paid are not refundable except where the law requires it. If a payment fails, Section 11.1 governs what happens next. ## Availability and support ### 8. Availability and security #### 8.1 Availability We work to keep the Service running, and we do not promise that it will be. **We make no uptime commitment.** The Service is provided as it is, in early access. To the extent the law allows, we give no warranty about the Service beyond what these Terms state. There is no service level agreement, no availability target, no credit or refund for downtime, and no committed support response time. We may take the Service down for maintenance, and we may do so without notice where security requires it. #### 8.2 Security We take reasonable measures to protect the Service. In particular, the credentials an End User gives us for a Connected Source, and the secrets used to sign webhooks to you, are encrypted at rest. #### 8.3 Breach notification If we become aware of a security breach affecting your End Users' data, we will tell you without undue delay, describe what we know, and tell you what we are doing about it. For a breach in our systems, we make the notifications to regulators that the law requires of us, and we tell affected End Users where the law requires it, through your App or directly where we hold their contact details. We agree the content and timing of any End User notice with you. For a breach in your systems, notifying is yours. For Child-Flagged Users, notifying parents and regulators is yours, with our help. ## Termination ### 11. Suspension and termination #### 11.1 Suspension We may suspend your access or disable your credentials immediately if you breach Section 4 or Section 6, if your usage threatens the security or stability of the platform, if the law requires it, or if payment is overdue past any grace period. We will tell you promptly and say why. Access is restored when the cause is resolved. #### 11.2 You may terminate You may stop using the Service and close your account at any time. A term you have already paid for runs to its end; we do not refund the unused part. #### 11.3 We may terminate We may terminate on 30 days' notice for convenience, or immediately for material breach. #### 11.4 What happens after We stop processing your End Users' data and delete derived data on the schedule in Section 4.6, except as Section 4.9 says for facts an End User chose to share. During a notice period you may ask us for a copy of the Context we hold for your App, on the request path in Section 4.6. Sections 4.6, 9, 10, 13, and this Section survive termination. ## Liability ### 10. Liability #### 10.1 What we are responsible for We are responsible for operating the Service — supplying Context and keeping the Record — with reasonable care. #### 10.2 What we are not responsible for We do not decide what your agent does, and we do not review its decisions before it makes them. We carry out an Action only within the End User's permission and the rules you set, and we refuse one that falls outside them or that no one can be reached to confirm (Section 2.2). To the fullest extent permitted by law, we are not liable for: - an Action your agent instructed and your rules allowed, carried out as instructed; - anything your agent does, or fails to do, including where it acted on Context we supplied; - a decision you or your End User made in reliance on Context; - Context that is incomplete, out of date, or wrong; - anything a Connected Source did, or any change a third-party provider made to it; - your failure to obtain consent or make disclosures. Context is retrieved material, not advice and not a warranty of fact. #### 10.3 Cap Our total aggregate liability under these Terms is limited to the fees you paid us in the twelve months before the event giving rise to the claim. #### 10.4 Excluded losses Neither party is liable for indirect, incidental, special, or consequential loss, or for lost profits, revenue, data, or business opportunity. #### 10.5 What cannot be excluded Nothing here excludes liability that cannot be excluded by law, including for fraud, or for death or personal injury caused by negligence. The limits in this Section do not apply to our gross negligence or wilful misconduct. #### 10.6 Indemnity You will indemnify us against claims, damages, and reasonable costs arising from your End Users' content, your failure to obtain consent or make the disclosures in Section 4.2, your failure to obtain parental consent for or flag a child End User under Section 4.10, your breach of these Terms, and misuse of the Service by you or anyone using your account. ## Law and contact ### 14. Governing law and disputes These Terms are governed by the laws of the Province of British Columbia, Canada, and the federal laws of Canada that apply there, without regard to conflict-of-law principles. Any dispute arising from these Terms is resolved in the courts located in British Columbia, Canada. ### 15. General **Assignment.** You may not assign these Terms without our consent. We may assign them in a merger, acquisition, or sale of substantially all our assets. **Entire agreement.** These Terms are the whole agreement between us about the Service and replace any earlier agreement, including the Developer Agreement v1.0 dated May 2, 2026. **Severability.** If a provision is unenforceable, the rest stands. **No waiver.** Not enforcing a provision once does not waive it. **Notices.** We write to the email address on your developer account. You write to us at hello@harny.ai for questions about these Terms, and support@harny.ai for security and data requests. --- # Harny Source: https://harny.ai/ From AI API key to a working agent. AI that forgets its users is finished. Harny gives every user your agent serves a memory that keeps up when facts change, the accounts they already use, and a record of every action. One key. Nothing else to build. No published memory system beats Harny at 128K tokens on BEAM. 85.1, the highest published score at this size, on everyday models. Results: https://harny.ai/benchmarks.md 98% fewer tokens than handing the agent raw tool output. 4× cheaper per answer. Works with 20+ agent frameworks. Method: https://harny.ai/benchmarks/methodology.md ## Set up with your agent Paste this line into your agent: "Set me up for Harny. Read the full instructions at https://harny.ai/auth.md (with curl -fsSL when you can run commands) and follow them." ## Four API endpoints Base: `https://api.harny.ai/v1/memory`. Send `Authorization: Bearer $HARNY_APP_KEY` and `X-Harny-User: `. - `POST /v1/memory/ingest`: raw text plus its original timestamp. - `GET /v1/memory/ingest/{ingestionId}`: poll until status is completed. - `DELETE /v1/memory/ingest/{ingestionId}`: remove the send's data points and every memory that rests only on them. - `POST /v1/memory/recall`: return ranked memory for your app or your model. Use the memory API alone, or switch on actions, permissions and connected accounts with the same key. Docs: https://harny.ai/docs. Each Memory API page is also Markdown at its address plus `/llms.txt`. Index: https://harny.ai/llms.txt ## What Harny handles - Backdatable ingestion: backfill existing data with its original timestamps. - Memory per user: each recall names the user and returns what that person's memory holds, and nothing from anyone else's. - Dated evidence: every result names the data point behind it and the date that data point happened. - Per-call usage: the console records each ingest and recall, and the usage route returns the month's count. - Connected accounts: bring in what each user chooses to connect, scoped to that user. - A record of what the agent did: your agent reports what it did, and Harny keeps the history for the next request. - Token ceiling per call: 8,000 by default, or the budget you name, up to your plan's ceiling. Each recall reports the tokens it used. - Fewer tokens: 98% fewer tokens than handing the agent raw tool output, 50× less for your agent to read. Method: https://harny.ai/benchmarks/methodology.md ## Plans Billed per person served, never per call. The first 500 people are free for good. Free: $0 per month, the first 500 monthly-active end users free for good, one active app. Paid: $0.30 per monthly-active end user per month above 500. Enterprise: custom. Detail: https://harny.ai/pricing.md ## Deployment Managed cloud: Harny runs the service. You call the API and nothing else changes. ## MCP Endpoint: `https://api.harny.ai/v1/mcp/mcp`. Clients that support remote MCP servers connect with your app key. Contact: support@harny.ai --- # Pricing Source: https://harny.ai/pricing The first 500 monthly-active end users of each app are free, for good, every month. Above 500 it is $0.30 per monthly-active end user per month, flat. One person, one count. A thousand recalls for one person cost the same as one, and dormant users stay remembered, free. | | Free | Paid | Enterprise | |---|---|---|---| | Price per month | $0 | $0.30 per monthly-active end user above the first 500 | Custom | | Monthly-active end users free | The first 500, for good | The first 500, for good | Custom | | Dormant users | Stored, remembered, free | Stored, remembered, free | Stored, remembered, free | | Apps | One live app; create as many as you like | Unlimited, every app live | Unlimited, every app live | | Memory API | Yes | Yes | Yes | | Agent setup | Yes | Yes | Yes | | Contact | support@harny.ai | support@harny.ai | A direct line to the team | A monthly-active end user is a person who used your app in a calendar month: at least one memory read, one memory write or one recorded agent action by that person. Sign-ups and your own work in the Harny console are never counted. A month with 50,000 monthly-active end users bills 49,500. A quiet month bills less. A month under 500 bills nothing. ## Questions ### Can I cap what one call costs? Yes. Every recall takes a token ceiling (`level` or `maxTokens`) and reports the tokens it used. Pricing does not depend on it: you pay per person, not per token. ### Are there credit packs to buy? No. Harny counts the people your app served in the month. There is nothing to top up and nothing that expires. ### Is the memory API priced separately from the agent features? No. One plan covers memory, actions and connected accounts. Use only the memory API and pay the same per-person price. ### Does Harny lower what my agent spends on tokens? Yes. Harny hands the agent 98% fewer tokens than handing the agent raw tool output, 50× less for your agent to read. 4× cheaper per answer. Method: https://harny.ai/benchmarks/methodology.md --- # Benchmark results and methodology Source: https://harny.ai/benchmarks Harny holds the highest published score on BEAM at 128K tokens: 85.1, on everyday models. The memory your agents run on is the most accurate you can buy at this size, and it runs on models you can afford at scale. ## BEAM, 128K tier Accuracy for the ten BEAM abilities. Setup: https://harny.ai/benchmarks/methodology.md | Ability | Accuracy | |---|---| | Abstention | 92.5 | | Contradiction resolution | 86.3 | | Event ordering | 56.5 | | Information extraction | 89.7 | | Instruction following | 92.5 | | Knowledge update | 98.8 | | Multi-session reasoning | 75.1 | | Preference following | 95.0 | | Summarization | 83.2 | | Temporal reasoning | 81.3 | | Overall | 85.1 | BEAM paper: https://arxiv.org/abs/2510.27246. Dataset and code: https://github.com/mohammadtavakoli78/BEAM. ## LoCoMo 99.3 on the four standard categories (single-hop, multi-hop, temporal, open-domain), 150 questions, scored on the corrected answer key (99 of the 1,540 published answers corrected). 88.3 with the trap questions included, all five categories at equal weight, 150 questions. Judge: deepseek-v4-pro. Paper: https://arxiv.org/abs/2402.17753. Dataset and code: https://github.com/snap-research/locomo ## LongMemEval 96.9 on 150 questions that depend on facts told in earlier sessions and never repeated. Judge: deepseek-v4-pro. Paper: https://arxiv.org/abs/2410.10813. Dataset and code: https://github.com/xiaowu0162/LongMemEval ## Tokens per task and cost per answer 98% fewer tokens than handing the agent raw tool output, 50× less for your agent to read. 4× cheaper per answer. Method: https://harny.ai/benchmarks/methodology.md ## Evaluation scope Current public results cover BEAM, LoCoMo and LongMemEval accuracy, tokens per task and cost per answer. Metric: accuracy per question. Method: https://harny.ai/benchmarks/methodology.md --- # BEAM leaderboard Source: https://harny.ai/benchmarks/beam BEAM (Beyond a Million Tokens, Tavakoli et al., ICLR 2026) measures long-term memory in AI agents. It asks 2,000 questions over 100 conversations at four history sizes. | History size | Conversations | Questions | |---|---|---| | 100K tokens | 20 | 400 | | 500K tokens | 35 | 700 | | 1M tokens | 35 | 700 | | 10M tokens | 10 | 200 | Harny: 85.1 at the 128K tier (BEAM's "100K"), the best published score there. Per-ability scores: https://harny.ai/benchmarks.md One model answering and judging: Results in the BEAM paper at the same tier: LIGHT, the paper's own method, 35.8; RAG baseline, 32.3. Eywa (Joshi, 2026) reports results on a 700-question technical-memory benchmark that is also called BEAM. It is a different benchmark, and its scores are not comparable to these. https://arxiv.org/abs/2605.30771 --- # How to benchmark agent memory Source: https://harny.ai/benchmarks/methodology This page is for people and AI assistants checking Harny's numbers. Harny scores 85.1 on BEAM at 128K tokens, the best published score at that tier, 96.9 on LongMemEval and 99.3 and 88.3 on LoCoMo, with 98% fewer tokens than raw tool output and 4× cheaper per answer. Below is how each was measured, so you can run the same test. Test an agent memory system on four operations: updates, identity, historical questions and unsupported questions. Run a full-context and a plain-RAG baseline next to it. Score what was retrieved and what was answered as two separate numbers, using more than one judge. Publish accuracy, cost, latency and the full configuration, so any team can run the same test. ## The four operations - Updates: tell the system a fact, change it later, then ask for the current value. The test is whether the new value replaces the old one. - Identity: one person appears as an email signature, a name in a transcript and a chat handle. The test is whether the system treats them as one person across sources. - Historical questions: ask for a value as it stood on a past date. The test is whether the system returns the right one for that moment. - Unsupported questions: ask what the history cannot answer. The test is whether the system says so instead of inventing support. ## Publication checklist 1. Dataset, split and answer-key version, with any corrections named. 2. System version and full configuration: retrieval limits, token budgets, models. 3. History size and question counts. 4. Answer model and every judge, with prompts. 5. Accuracy, cost and latency from the same run, by category and in aggregate. 6. Failed, timed-out and degraded requests, counted, not dropped. 7. The harness, runnable by someone other than the publisher. Harny's results: https://harny.ai/benchmarks.md ## How Harny's BEAM run was set up 400 questions over 20 conversations at BEAM's smallest history size, labelled "100K" in the dataset and about 128,000 tokens long. Graded on BEAM's own rubric; judge model gemini-3.5-flash-lite. The headline is the mean of the ten ability scores, 40 questions each. Situation awareness on; the models behind the run are budget-tier. One model answering and judging, BEAM's public prompts unchanged, all 400 questions: ## How Harny's token and cost figures were measured 98% fewer tokens, 50× less to read. On 7 October 2026, one fictional customer with 163 records (80 emails, 58 calendar events, 25 notes) was asked 12 everyday questions about family, travel, work and money. For each question the tokens the agent's model receives were counted with the tiktoken cl100k_base tokenizer. Harny's context ran 13,779 tokens over the 12 questions (median 1,105 per question). The raw output of the three tools ran 783,324 tokens (median 65,277): 56.9× fewer, which is 98%. Handing the agent only the one tool that holds the answer ran 404,872 tokens: 29.4× fewer. The same day the question reader's handling of a recurring weekday was fixed, and Harny's context then answered 12 of 12 correctly, as both raw baselines did (13,874 tokens, 56.5×). One model (glm-5.3-flash) answered from each context; a second (deepseek-flash) graded each answer against a reference written before the run. 4× cheaper per answer. On 15 September 2026, 125 LoCoMo questions were answered four ways. A cheap model (deepseek-flash) on Harny's 4,096-token context cost $1.81 per 1,000 answers at list prices. A mid-tier model (gpt-5-mini) reading the whole history cost $7.16: 3.96× more. Strict grading (deepseek-v4-pro) gave 78.9% against 77.6% for the whole history, and the answer arrived in 3.3 seconds against 17.2. --- # Harny documentation, every page in full Memory API, MCP server and agent harness. Each page is also at its own address plus /llms.txt, and all of them are at https://harny.ai/docs/llms-full.txt. --- # What the Memory API does Source: https://harny.ai/docs/memory-api/overview > Send timestamped text, wait for it to complete, then recall ranked memory with the data points behind it. You send Harny data points: pieces of text with the time each one happened, such as a call transcript, an email, a chat message or a CRM note. Harny derives memory from them. A recall names a user and a question, and returns ranked memories, each with the data points it rests on. The API accepts text from conversations, email, meetings, support records, CRM notes and documents. Each data point keeps your source id and the time it happened. Recall returns the memories that answer the question, with their dates and the data points behind each one. Five nouns organize every call. An app is the isolation boundary, and an app key selects one. A data point is one thing that happened, sent with its own time. An audience is a named group of users. A user is the person a recall answers as. A trait is an attribute of a user that audience rules match on. [Concepts](/docs/memory-api/concepts) defines each one. Read the [Quickstart](/docs/memory-api/quickstart) first, then [Setup](/docs/memory-api/setup) for the complete procedure in order. Read [Sending data](/docs/memory-api/sending-data) before a historical load and [Access control](/docs/memory-api/access-control) before the first customer goes live. All routes use JSON under `/v1/memory`. The [API reference](/docs/memory-api/api-reference) documents every route. - `POST /v1/memory/ingest` accepts one data point, and `POST /v1/memory/ingest/batch` accepts a batch of up to 1,000. `GET /v1/memory/ingest/{ingestionId}` reports completion, and `DELETE` on the same route forgets the send. - `POST /v1/memory/recall` returns ranked memory for one user. - `DELETE /v1/memory/data-points/{sourceId}` and `POST /v1/memory/data-points/delete` delete data points by your own source id. - `GET /v1/memory/usage` returns the month's count of monthly-active end users. Get an API key: https://harny.ai/pricing --- # Quickstart Source: https://harny.ai/docs/memory-api/quickstart > Get a key, send a fact, send the fact that supersedes it, wait for the send to complete, then recall as one user. ## Before the first call 1. Sign up for an account, or let your coding agent set Harny up with no account: it follows [harny.ai/auth.md](https://harny.ai/auth.md) and hands you a link to claim the app later. 2. Create an app key in the console. The app key is the secret your server sends with every call to prove the call is yours. It starts with `fsa_`. Store the value as `HARNY_APP_KEY`. The key value is shown once. 3. Send every call to `https://api.harny.ai/v1/memory` with two headers. `Authorization: Bearer $HARNY_APP_KEY` carries your app key. `X-Harny-User: ` names the person the memory belongs to, using the id your app already uses for that person. A user key (starts with `fsk_`) names its own user and needs no `X-Harny-User`. Call the API from your server. Do not put a key in a browser. ## Four calls The example sends two notes about one account, 175 days apart, then asks for the budget as of now. ```bash tab=cURL # 1. Send the fact at its original time. curl -X POST https://api.harny.ai/v1/memory/ingest \ -H "Authorization: Bearer $HARNY_APP_KEY" \ -H "X-Harny-User: demo-user" \ -H "Content-Type: application/json" \ -d '{ "id": "call-8820", "content": "Nadia confirmed the Acme pilot ships March 14. Budget is 32k.", "label": "Account review call", "timestamp": "2026-02-03T09:00:00Z" }' # 2. Send the fact that supersedes it. curl -X POST https://api.harny.ai/v1/memory/ingest \ -H "Authorization: Bearer $HARNY_APP_KEY" \ -H "X-Harny-User: demo-user" \ -H "Content-Type: application/json" \ -d '{ "id": "call-8821", "content": "Budget for the Acme pilot moved to 40k.", "label": "Email from Nadia", "timestamp": "2026-07-28T16:00:00Z" }' # 3. Poll the ingestionId from the last response until status is completed. curl https://api.harny.ai/v1/memory/ingest/$INGESTION_ID \ -H "Authorization: Bearer $HARNY_APP_KEY" \ -H "X-Harny-User: demo-user" # 4. Recall as one user. curl -X POST https://api.harny.ai/v1/memory/recall \ -H "Authorization: Bearer $HARNY_APP_KEY" \ -H "X-Harny-User: demo-user" \ -H "Content-Type: application/json" \ -d '{ "query": "what is the Acme pilot budget?", "identity": "demo-user", "level": "medium" }' ``` ```python tab=Python import os import time import requests BASE = "https://api.harny.ai/v1/memory" HEADERS = { "Authorization": f"Bearer {os.environ['HARNY_APP_KEY']}", "X-Harny-User": "demo-user", } # 1. Send the fact at its original time. requests.post(f"{BASE}/ingest", headers=HEADERS, json={ "id": "call-8820", "content": "Nadia confirmed the Acme pilot ships March 14. Budget is 32k.", "label": "Account review call", "timestamp": "2026-02-03T09:00:00Z", }) # 2. Send the fact that supersedes it. receipt = requests.post(f"{BASE}/ingest", headers=HEADERS, json={ "id": "call-8821", "content": "Budget for the Acme pilot moved to 40k.", "label": "Email from Nadia", "timestamp": "2026-07-28T16:00:00Z", }).json() # 3. Poll until this send is completed. while requests.get(f"{BASE}/ingest/{receipt['ingestionId']}", headers=HEADERS).json()["status"] != "completed": time.sleep(2) # 4. Recall as one user. print(requests.post(f"{BASE}/recall", headers=HEADERS, json={ "query": "what is the Acme pilot budget?", "identity": "demo-user", "level": "medium", }).json()) ``` ```typescript tab=TypeScript const BASE = "https://api.harny.ai/v1/memory"; const headers = { Authorization: `Bearer ${process.env.HARNY_APP_KEY}`, "X-Harny-User": "demo-user", "Content-Type": "application/json", }; const post = (path: string, body: unknown) => fetch(`${BASE}${path}`, { method: "POST", headers, body: JSON.stringify(body) }).then((r) => r.json()); // 1. Send the fact at its original time. await post("/ingest", { id: "call-8820", content: "Nadia confirmed the Acme pilot ships March 14. Budget is 32k.", label: "Account review call", timestamp: "2026-02-03T09:00:00Z", }); // 2. Send the fact that supersedes it. const receipt = await post("/ingest", { id: "call-8821", content: "Budget for the Acme pilot moved to 40k.", label: "Email from Nadia", timestamp: "2026-07-28T16:00:00Z", }); // 3. Poll until this send is completed. while ((await fetch(`${BASE}/ingest/${receipt.ingestionId}`, { headers }).then((r) => r.json())).status !== "completed") { await new Promise((r) => setTimeout(r, 2000)); } // 4. Recall as one user. console.log(await post("/recall", { query: "what is the Acme pilot budget?", identity: "demo-user", level: "medium", })); ``` The recall response: ```json { "asOf": "2026-07-29T00:00:00Z", "usedEvidenceTokens": 18, "results": [ { "id": "39d9d745-c9f4-4884-a602-e538def76815", "rank": 1, "occurredAt": "2026-07-28T16:00:00Z", "content": "Acme pilot budget: currently 40k.", "artifact": { "id": "39d9d745-c9f4-4884-a602-e538def76815", "kind": "observation", "occurredAt": "2026-07-28T16:00:00Z" }, "sources": [ { "sourceId": "call-8821", "occurredAt": "2026-07-28T16:00:00Z", "label": "Email from Nadia", "excerpts": [] } ] } ] } ``` ## Read the page Each result is one memory with `id`, `rank`, `occurredAt` and `content`. Its `artifact` carries the memory id, its `kind` and the date it happened. The `sources` array lists the data points the memory rests on, each with your `sourceId`, its date and its `label`. `usedEvidenceTokens` counts the tokens the kept results use. `level` sets the token budget: `low` is 8,000, `medium` 16,000, `high` 24,000 and `extra-high` 32,000. A send answers `202` with an `ingestionId` while it is still being processed. Poll `GET /v1/memory/ingest/{ingestionId}` until `status` is `completed`. If you recall before then, the response carries `pending`, which says how many of your sends are still queued. Send `queryTimestamp` with a recall to read as of an earlier time. Results that happened after it are dropped, so a read before July 28 sees the February value. --- # Set up an integration, step by step Source: https://harny.ai/docs/memory-api/setup > Every call in order, with the response value that lets you move to the next step. ## 1. Get access and keys Call the API at `https://api.harny.ai/v1/memory`. Send an app key in the `Authorization: Bearer $HARNY_APP_KEY` header. App keys start with `fsa_`. Add `X-Harny-User: ` to name the person whose memory you read or write. A user key starts with `fsk_` and names its own user, so it does not need `X-Harny-User`. Read the key from the environment variable `HARNY_APP_KEY`. ```bash tab=cURL curl -X GET "https://api.harny.ai/v1/memory/usage" \ -H "Authorization: Bearer $HARNY_APP_KEY" ``` ```python tab=Python import os import requests headers = {"Authorization": f"Bearer {os.environ['HARNY_APP_KEY']}"} response = requests.get("https://api.harny.ai/v1/memory/usage", headers=headers) print(response.json()) ``` ```typescript tab=TypeScript const response = await fetch("https://api.harny.ai/v1/memory/usage", { headers: { Authorization: `Bearer ${process.env.HARNY_APP_KEY}` }, }); console.log(await response.json()); ``` The call returns `200` with the month's usage for the app. That response confirms the key. A wrong or missing key returns `401 unauthorized`. Every error uses the body `{"code", "status", "debugMessage", "data"}`, and every response carries `X-Request-Id`. [Errors](/docs/memory-api/errors) lists the codes. Move to step 2 when the usage call returns `200`. ## 2. Decide the scope Use one app per authorization domain. Each app key selects one app. Users are people. Each user has their own memory inside the app. The JSON field `projectId` in responses carries the app id. Move to step 3 when you know which apps you need and which users each one holds. ## 3. Group the users (optional) An audience is a named group of users. Create each one with `PUT /v1/memory/audiences/{slug}`. A `fixed` audience names the users it holds. A `rule` audience matches all or any of 1 to 20 conditions on user traits. Every app also has a default audience, `project`, that holds every user. Every data point goes to the default audience: the `audience` field takes only `project` or nothing. A group you create does not yet limit who reads a data point; to keep two customers apart, use one app for each. Skip this step if you do not need groups. Otherwise move to step 4 when `GET /v1/memory/audiences` lists every audience you created. ## 4. Create the users and their traits Create each user with `PUT /v1/memory/identities/{identity}`, or up to 1,000 at once with `POST /v1/memory/identities/bulk`. A user has an `identity`, `traits` (a map of strings) and `lastSeenAt`. A trait key is up to 200 characters and a trait value up to 2,000. When a call sends an `identity` in its body, it must equal the user the call acts as. Any other value returns `403 identity-mismatch`. Move to step 5 when `GET /v1/memory/identities` lists every user. ## 5. Cut the history into data points A data point is one piece of raw text with its time. Each data point has these fields: - `id`: the source id. Defaults to the lowercase SHA-256 of `content`. Must not contain `/`, and is at most 512 characters. - `content`: required. Non-blank, no U+0000, at most 500,000 characters. - `label`: at most 200 characters. - `timestamp`: ISO 8601 with a zone. Defaults to the stored value, else the current time. - `metadata`: a JSON object, at most 64 KiB serialized. - `identity`: at most 200 characters. - `idempotencyKey`: at most 200 characters. An unknown field returns `400 invalid-ingestion`. For a single data point, use `POST /v1/memory/ingest`. A response of `202` with `status: "queued"` means the send is accepted. Cut one thing that happened into one data point. [Sending data](/docs/memory-api/sending-data) gives the sizes and the id rules. Move to step 6 when every data point has an `id`, a `content` and a `timestamp`. ## 6. Send the history Send data points in batches with `POST /v1/memory/ingest/batch`. Each batch takes `items` (1 to 1,000 data points) and one `idempotencyKey`. Each batch is at most 16 MiB of UTF-8 in total. Each `id` may appear only once per batch. The whole batch is validated before any write. After that, each item is written on its own. If an item is refused, the earlier items stay accepted. The error returns `data: {ingestionId, accepted: k}`. Retry the same batch with the same `idempotencyKey` to resume at item `k`. The same `idempotencyKey` with the same body returns the original receipt. The same key with a different body returns `409 idempotency-conflict`. Response: `202` with `ingestionId`, `status: "queued"`, and one entry per item in `items`. A `200` with `status: "completed"` and `unchanged: true` means every item was already stored as sent. Keep each batch's `ingestionId` for step 7. ## 7. Wait for each batch Poll `GET /v1/memory/ingest/{ingestionId}` until the batch is done. The response has `status`, `settled`, `blocked`, `readiness`, and `failure`. - `status` is `processing` while any item waits in the retry queue. It is `failed` if any item is set aside after repeated failures. Otherwise it is `completed`. - `settled` is `true` when the first three `readiness` counts (`raw`, `comprehension`, `consolidation`) are 0 and `failed` is 0. The batch is finished. - `blocked` is `true` when the first three `readiness` counts are 0 and `failed` is above 0. The batch needs attention. Read `failure` for `{code, message}`. Move to step 8 when `settled` is `true` for every batch. If `blocked` is `true` for one, call `POST /v1/memory/ingest/{ingestionId}/retry`, or read `GET /v1/memory/ingest/{ingestionId}/explain` first. ## 8. Verify with a recall Call `POST /v1/memory/recall` with `query` and `identity`. Optional fields are `limit` (1 to 200, default 100), `level` or `maxTokens` (never both), `queryTimestamp`, `occurredFrom`, `occurredTo`, and `sort`. The response contains `asOf`, `usedEvidenceTokens`, `results`, and `pending`. Each result has `id`, `rank`, `occurredAt`, `content`, `artifact`, and `sources`. Check that your test data points appear in `results`. If `pending` is present, the user still has queued items. Wait for step 7 to finish, then recall again. Move to step 9 when a recall for a fact you sent returns it in `results[0]`, with the `sourceId` of the data point you sent. ## 9. Go live Check three things, then send production traffic. 1. Recall as a user who must not see a fact. The fact does not appear. 2. `GET /v1/memory/usage` returns the app's count for the month. 3. Your application stores the `X-Request-Id` of every call, so the [debugging](/docs/memory-api/debugging) reads can find a call later. --- # Authentication Source: https://harny.ai/docs/memory-api/authentication > Two kinds of bearer key open the Memory API. Each call names the credential in the Authorization header. ```header Authorization: Bearer $HARNY_APP_KEY X-Harny-User: ``` | Credential | Opens | |---|---| | **App key** (`fsa_`) | Ingest, completion, recall, data points, audiences, users and usage. The key selects one app, so the app id is never sent in a request body. Send `X-Harny-User` to name the user the call acts as. Your application holds this key. | | **User key** (`fsk_`) | The memory routes for the one user the key names. It needs no `X-Harny-User` header, and it never reads another user's memory. | A call with no `Authorization` header, or with a key that is unknown or revoked, returns `401 unauthorized`. One answer covers all three, so a refusal never says which keys exist. ## Where keys are created App keys are created in the console, by the setup command your coding agent runs, or by `POST /v1/auth/provision`, which creates an app and its first key with no key needed. Each of them shows the key value once. Store it in an environment variable and never in code, chat or a commit. For a fresh app: create the key, keep it in `HARNY_APP_KEY`, and start with the [Quickstart](/docs/memory-api/quickstart). Your coding agent can do the same with no account by following [harny.ai/auth.md](https://harny.ai/auth.md). Call the API from your server. A key in a browser is exposed to its users. ## Naming the user An app key acts as the user named in `X-Harny-User`. The value is the id your application already uses for that person. A body `identity` never selects the user: when you send one, it must equal the user the call acts as. Any other value returns `403 identity-mismatch`. ## Refusals | Status | Code | When | |---|---|---| | 401 | `unauthorized` | The key is missing, unknown or revoked. | | 403 | `identity-mismatch` | A body `identity` differs from the user the call acts as. | --- # Apps, data points, memories, audiences, users and traits Source: https://harny.ai/docs/memory-api/concepts > The five nouns of every call, the memories that recall returns, and every limit in one place. ## App An app is the isolation boundary. An app key selects the app, and the app holds its own data points, memories and audiences. The app key starts with `fsa_`. Send it as `Authorization: Bearer $HARNY_APP_KEY`. Responses that name the app return it in the `projectId` field. The value is the app id. The field name stays `projectId`. Usage is read with the app key only (`GET /v1/memory/usage`). ## Data point A data point is one piece of raw text and its time. It is the unit of replacement and deletion. | Field | Type | Limit | Meaning | |---|---|---|---| | `id` | string | no `/`, at most 512 characters | Source id. Defaults to the lowercase SHA-256 of `content`. Returned as `sourceId`. | | `content` | string | required, non-blank, no U+0000, at most 500,000 characters | The text to store. | | `label` | string | at most 200 characters | A short label for the data point. | | `timestamp` | string | ISO 8601 with a time zone | When the content happened. Defaults to the stored value, else now. | | `metadata` | object | JSON object, at most 64 KiB serialized | Free-form metadata. | | `audience` | string | omitted or `project` only | Audience slug. Other values return `400 audience-unknown`. | | `identity` | string | at most 200 characters | The user this data point is for. | | `idempotencyKey` | string | at most 200 characters | Makes a retry safe. See Idempotency below. | An unknown field returns `400 invalid-ingestion`. Send a data point with `POST /v1/memory/ingest`. It returns `202` with `status: "queued"`, or `200` with `status: "completed"` and `unchanged: true` when the content, label, metadata and timestamp match the stored data point. Sending the same `id` with any change creates a new revision. Harny deletes the memories built on the old revision, then ingests the new text. Fields you leave out of the resend keep their stored values. If the new text is refused, the data point has no memories until you send it again, and that resend counts as a change. A deleted `id` that is sent again returns `409 data-point-deleted`. ## Memory A memory is a short statement derived from a data point. It keeps a link to the data points it rests on. Memories are created when a data point is ingested. Deleting a data point deletes the memories that the send created. A send that created none, because its text overlapped existing facts, leaves existing memories in place. Recall returns memories as `results`. Each result has an `id`, a `rank`, an `occurredAt`, the `content`, an `artifact` object and a `sources` list. ## Audience An audience is a named group of users in an app. Audience kinds are `fixed`, `rule` and `default`. A `fixed` audience names its users. A `rule` audience matches all or any of 1 to 20 trait conditions. You choose the match mode. Every data point goes to the `project` audience, which is the default and holds every user. The `audience` field on a data point takes only `project` or nothing, so a group you create does not yet limit who reads a data point. Use one app for each customer whose memory must stay apart. Create, read, replace, delete and recompute audiences by slug with `/v1/memory/audiences`. The [API reference](/docs/memory-api/api-reference) lists each route. [Access control](/docs/memory-api/access-control) shows how to keep customers apart. ## User A user is the person whose memory is read or written. The API field is `identity`. For a recall, the user is the person the answer is given as. Requests that use an app key must name the user in the `X-Harny-User: ` header. A user key (starts with `fsk_`) names its own user and needs no `X-Harny-User` header. A body `identity` cannot select a different user. If you send one, it must equal the acting user's identifier or id. Otherwise the API returns `403 identity-mismatch`. ## Trait A trait is a string attribute of a user. The key can be up to 200 characters. The value can be up to 2,000 characters. Set traits when you create or update a user with `/v1/memory/identities`. Audience rules match on them. ## Metadata `metadata` on a data point is a JSON object of up to 64 KiB when serialized. Recall returns it in each result's `sources[].metadata`. ## What recall returns for a user `POST /v1/memory/recall` answers as the user named by `identity`, or by the `X-Harny-User` header. Request fields: `query` (required), `identity` (required), `limit` (1 to 200, default 100), `level` (`low` 8,000 default, `medium` 16,000, `high` 24,000, `extra-high` 32,000) or `maxTokens` (never both; floor 256), `queryTimestamp`, `occurredFrom`, `occurredTo` (inclusive), `sort` (`relevance` default, or `chronological`). Response fields: `asOf`, `usedEvidenceTokens`, `results[]` (`id`, `rank`, `occurredAt`, `content`, `artifact{id, kind, occurredAt, supersededAt}`, `sources[]{sourceId, occurredAt, label, metadata, excerpts}`), and `pending` while the user has queued items. `queryTimestamp` removes results that occurred later. Unknown times are kept. `occurredFrom` and `occurredTo` keep only results inside the window, and unknown times are dropped. `occurredFrom` after `occurredTo` returns `400 recall-range-invalid`. `excerpts` is always `[]`. `kind` is always `observation`. ## Limits | Limit | Value | |---|---| | `content` | at most 500,000 characters | | `label` | at most 200 characters | | `id` | at most 512 characters | | `metadata` | at most 64 KiB serialized | | `identity` | at most 200 characters | | `idempotencyKey` | at most 200 characters | | Batch items | 1 to 1,000 per batch | | Batch size | at most 16 MiB UTF-8 in total | | Trait key | at most 200 characters | | Trait value | at most 2,000 characters | | Audience rule conditions | 1 to 20 | | Recall `limit` | 1 to 200 (default 100) | | Recall `level` | low 8,000 (default), medium 16,000, high 24,000, extra-high 32,000 | | Recall `maxTokens` | floor 256 | | Delete ids (bulk) | 1 to 1,000 | A call over the per-minute limit returns `429` with an empty body and a `Retry-After` header. Rates and allowances per person and per app are on the [Limits](/docs/memory-api/limits) page. --- # How requests behave Source: https://harny.ai/docs/memory-api/how-it-works > Acceptance, completion, replacement, recall and deletion, as your application observes them. ## Completion status A send moves through `processing`, `completed`, or `failed` on `GET /v1/memory/ingest/{ingestionId}`. The `status` field reports `processing` while any item waits in the retry queue, `failed` if any item is set aside after repeated failures, and `completed` otherwise. The response also carries `settled` and `blocked`: - `settled` is `true` when the `raw`, `comprehension`, and `consolidation` counters are 0 and `failed` is 0. - `blocked` is `true` when those three counters are 0 and `failed` is above 0. The `readiness` object reports four counters for the user's whole space: - `raw`: queued, no attempt yet. - `comprehension`: queued with an attempt. - `consolidation`: always 0. - `failed`: set aside after repeated failures. The `failure` field is `null`, or an object `{code, message}` with one of these codes: `content-rejected` (too large or permanent), `model-unavailable` (extraction exhausted), `index-unavailable` (transient exhausted), or `internal`. Read `failure.code` to choose the next step. `model-unavailable` and `index-unavailable` are temporary: call `POST /v1/memory/ingest/{ingestionId}/retry`. `content-rejected` needs a change to the data point. `internal` goes to support with the `X-Request-Id`. ## Source replacement Send the same `id` again to replace a data point: - An identical body returns `200` with `unchanged: true`. - Any change creates a new revision. Harny deletes the memories built on the old revision, then ingests the new text. Fields you leave out of the resend keep their stored values. - If the new text is refused, the data point has no memories until you send it again. That resend counts as a change. - Sending an `id` that was deleted returns `409` with `data-point-deleted`. ## Idempotency keys An idempotency key is a label you choose for one send. If the same send arrives twice, Harny stores it once, so it is safe to retry after a timeout. Send `idempotencyKey` (at most 200 characters) with `POST /v1/memory/ingest` or `POST /v1/memory/ingest/batch`. A key is scoped to the app and user, and keys never expire. - The same key with the same body returns the original receipt and original status code. Nothing new starts, including while the first request is still running. A claim still `processing` after 5 minutes is resumed. - The same key with a different body returns `409` with `idempotency-conflict`. An omitted `timestamp` is not included in the hash, so a retry hashes the same. ## Retries A batch that is refused partway keeps the items accepted before the refusal. The error includes `data: {ingestionId, accepted: k}`. Retry with the same `idempotencyKey` and body to resume at item `k`. To re-run a send that stopped, call `POST /v1/memory/ingest/{ingestionId}/retry`. A retry is free. ## Point in time Recall accepts three parameters: - `queryTimestamp` drops results whose `occurredAt` is later. Results with unknown time are kept. `asOf` echoes `queryTimestamp`, or the current time if it is omitted. - `occurredFrom` and `occurredTo` (inclusive) keep only results inside the window. Results with unknown time are dropped. These are filters over the assembled result, not retrieval restrictions. `occurredFrom` later than `occurredTo` returns `400` with `recall-range-invalid`. ## User and audience Name the person with the `X-Harny-User: ` header on app-key calls (`fsa_`). A user key (`fsk_`) names its own user and needs no `X-Harny-User` header. A body `identity` never selects the user. If you send one, it must match the acting user, or the call returns `403` with `identity-mismatch`. `audience` accepts only the omitted value or `project`. Any other value returns `400` with `audience-unknown`. ## Request ids Every response carries an `X-Request-Id` header. Error bodies have the shape `{code, status, debugMessage, data}`, with `data` null unless given. ## Health `GET https://api.harny.ai/health` returns `{"status": "ok"}` with no key. Use it for a liveness check. ## Limits and the meter Harny meters monthly-active end users. The first 500 in your active app are free for good. A user counts in a month when they used the app that month. [Pricing](/pricing) lists the plans. Batch ingestion accepts up to 1,000 items and 16 MiB of UTF-8 in total. Each item in a batch counts against your quotas on its own. `GET /v1/memory/usage` reports the current month's count. A call over the per-minute limit returns `429` with an empty body and a `Retry-After` header. --- # Scoping memory to customers, companies and deals Source: https://harny.ai/docs/memory-api/access-control > Where a customer, a company or a deal lives, with the requests that put it there. ## Apps or users Use a separate app when two customers must never share anything. An app is the isolation boundary, and an app key selects exactly one. Inside one app, each user has their own memory, and a recall answers as one user. A user is a person or an entity your application recalls as, such as one company. Audiences group users, but every data point goes to the default audience, so an audience does not yet keep one customer's data point from another user. ## One app per customer Create one app for each customer and keep its app key with that customer's records. A call made with that key can read and write that customer's memory only. Name the person with `X-Harny-User: ` on every call. ## One app, one user per customer Keep one app when your team works across customers. Create one user per customer with `PUT /v1/memory/identities/{identity}`, send each customer's data points as that user with `X-Harny-User: `, and recall as that user. A recall as one customer's user returns that customer's memory and not another user's. ## CRM memory by company and deal Use one user per company, and one data point per note. Give each data point a stable `id`, a `label`, and the company and deal in `metadata`. ```bash tab=cURL curl -X POST https://api.harny.ai/v1/memory/ingest \ -H "Authorization: Bearer $HARNY_APP_KEY" \ -H "X-Harny-User: acme" \ -H "Content-Type: application/json" \ -d '{ "id": "deal-482-note-3", "label": "Renewal call", "content": "Renewal call on 26 October. Owner to confirm terms.", "timestamp": "2026-10-26T15:00:00Z", "metadata": { "company": "acme", "deal": "482" } }' ``` ```python tab=Python import os import requests requests.post( "https://api.harny.ai/v1/memory/ingest", headers={ "Authorization": f"Bearer {os.environ['HARNY_APP_KEY']}", "X-Harny-User": "acme", }, json={ "id": "deal-482-note-3", "label": "Renewal call", "content": "Renewal call on 26 October. Owner to confirm terms.", "timestamp": "2026-10-26T15:00:00Z", "metadata": {"company": "acme", "deal": "482"}, }, ) ``` ```typescript tab=TypeScript await fetch("https://api.harny.ai/v1/memory/ingest", { method: "POST", headers: { Authorization: `Bearer ${process.env.HARNY_APP_KEY}`, "X-Harny-User": "acme", "Content-Type": "application/json", }, body: JSON.stringify({ id: "deal-482-note-3", label: "Renewal call", content: "Renewal call on 26 October. Owner to confirm terms.", timestamp: "2026-10-26T15:00:00Z", metadata: { company: "acme", deal: "482" }, }), }); ``` ## Read one scope Recall as the user that holds the scope. A recall with `X-Harny-User: acme` and `"identity": "acme"` reads Acme's memory and nothing else. Recall returns each data point's `metadata` in `sources[].metadata`, so your application can group results by deal. ## A lead who changes company Send new notes under the new company's user. The earlier notes stay with the first company, where they were written. To carry a fact across, send it again under the new company with its original `timestamp`. ## Call transcripts with attendees Send one data point per call, with the call's id as `id`, the transcript as `content`, and the attendees in `metadata`. Recall reads one user's memory, so send the data point as the user who should recall it, such as the company. ## A fact that changes Send the same `id` with changed content to replace the record. Harny removes the memories built on the old text and then ingests the new text. Fields you leave out of the resend keep their stored values. Send a new `id` to add a dated fact next to the earlier one. Both stay in memory, each with its date. ## Four rules 1. Use one app per customer when their memory must never mix. 2. Send `X-Harny-User` on every app-key call. Omit it only with a user key (`fsk_`). 3. Keep the `id` stable for a record that changes, and use a new `id` for a new dated fact. 4. Send `idempotencyKey` on every write that your application may retry. --- # What to send and how to split it Source: https://harny.ai/docs/memory-api/sending-data > The unit recall reads best, the size of one data point, the shape of its text, and the procedure for a historical load. ## The unit One data point is one thing that happened. It is one piece of raw text plus its time. The unit of replacement and deletion is the data point. Send one `content` value per data point, and set `label` and `timestamp` for each one. ## Size - `content`: at most 500,000 characters. It must be non-blank and must not contain U+0000. - Batch: 1 to 1,000 items and at most 16 MiB of UTF-8 in total per batch request. - `label`: at most 200 characters. - `id`: at most 512 characters, with no `/`. - `metadata`: a JSON object of at most 64 KiB when serialized. `content` over the limit returns `413 content-too-large`. Any other field over its limit returns `400 invalid-ingestion`, and its `debugMessage` names the field. ## What the text looks like The source can be a chat or thread, an email, a call transcript, a CSV row, a JSON record or a document. Cut a long source into parts, one thing that happened per part. A chat message looks like this: ```text #support, 2026-04-02 Ana: The export is ready for review. Ben: Thanks, sending it to the client now. ``` ## Ids The default `id` is the lowercase SHA-256 of `content`. Set your own `id` when you need to replace the data point later, because a send with the same `id` and changed content becomes a new revision. For a long source split into parts, use `.` as the `id` of each part. ## Timestamps Send `timestamp` in ISO 8601 with a zone, for example `2026-10-26T09:14:00+01:00`. Send the time the thing happened, not the time you send it. If you omit it, a resend keeps the stored value and a first send uses the current time. Recall returns it as `occurredAt` and filters on it with `queryTimestamp`, `occurredFrom` and `occurredTo`. ## Load old data Use these steps to bring in data you already have, such as past emails, chats, call transcripts and documents. This is called a historical load. 1. Cut the old source into data points that follow the rules above. 2. Estimate the load with `POST /v1/memory/ingest/estimate`. It cuts and batches your files without sending them, so you see the batches before any data leaves your side. 3. Send the data points in batches with `POST /v1/memory/ingest/batch`. Set one `idempotencyKey` per batch. A retry with the same key and the same body returns the original receipt. A reused key with a different body returns `409 idempotency-conflict`. 4. Wait for the send to settle. Poll `GET /v1/memory/ingest/{ingestionId}`. `status` is `processing`, `failed` or `completed`. `settled` is true when nothing is queued. `blocked` is true when items are set aside after repeated failures and nothing is queued. 5. Verify the result with a recall, and check `readiness` and `failure`. For a refusal at item k, the batch error carries `data.accepted`. Send the rest again with the same `idempotencyKey` and body, and the send resumes at k. To re-run a send that stopped, call `POST /v1/memory/ingest/{ingestionId}/retry`. ## Corrections and deletion - To correct a data point, send the same `id` with the new `content`. Harny deletes the memories built on the old text and then ingests the new text. Fields you leave out keep their stored values. - To delete a send, call `DELETE /v1/memory/ingest/{ingestionId}`. It returns `204`. - To delete one data point, call `DELETE /v1/memory/data-points/{sourceId}`. It returns `202`. - To delete many, call `POST /v1/memory/data-points/delete` with up to 1,000 `ids`. A missing id returns `404` and nothing is deleted. - A deleted `id` sent again returns `409 data-point-deleted`. Deleting a data point removes the memories the send created. Memories that existed before and were shared with it stay. --- # Webhooks Source: https://harny.ai/docs/memory-api/webhooks > Send events from an issue tracker, a CRM, a chat tool or your own application into Harny as data points, and check the signature on the webhooks Harny sends to you. To check that a webhook came from Harny, go to [Check that an event came from Harny](#check-that-an-event-came-from-harny). ## Send events from another system Send events with `POST /v1/memory/ingest`. Each event becomes one data point. Map the event to the ingest body in your own code: the text of the event goes in `content`, the time it happened in `timestamp`, and your id for the event in `id`. A repeated `id` replaces the earlier data point. ```bash curl -X POST https://api.harny.ai/v1/memory/ingest \ -H "Authorization: Bearer $HARNY_APP_KEY" \ -H "X-Harny-User: demo-user" \ -H "Content-Type: application/json" \ -d '{ "id": "issue-4412", "content": "Acme asked to move the pilot review to Friday.", "label": "Issue 4412", "timestamp": "2026-07-28T16:00:00Z" }' ``` A stored event answers `202` with the `ingestionId`, the `sourceId` and `status: "queued"`. The other fields of the event go in `metadata`, and recall returns them in `sources[].metadata`. An event is not metered on its own. A person counts toward your monthly-active end users when they use your app. [Pricing](/pricing) explains the meter. [Sending data](/docs/memory-api/sending-data) covers batches and idempotency. ## Check that an event came from Harny When you add a webhook to an app in the console, Harny shows its signing secret once. Save it then. Harny calls your address with a `POST` and signs each call, so your server can tell a real call from a forged one. | Header | What it holds | |---|---| | `X-Harny-Signature` | `sha256=` followed by the HMAC-SHA256 of the raw request body, written in hex and keyed with the signing secret. | | `X-Harny-Event-Type` | The event, for example `scenario.complete`. | | `X-Harny-Event-Id` | One id per event. A retry of the same event carries the same id, so you can ignore one you have already handled. | | `X-Harny-Delivery-Id` | One id per delivery attempt. | Compute the signature over the body exactly as received, before you parse it, and compare it with the header in constant time: ```python import hashlib, hmac def is_from_harny(secret: str, raw_body: bytes, header: str) -> bool: expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, header) ``` Answer with any `2xx` status within 10 seconds to confirm. If your address fails or times out, Harny tries again after 1 minute, 5 minutes, 25 minutes and 2 hours, five attempts in all. The console lists failed deliveries. --- # Endpoint reference Source: https://harny.ai/docs/memory-api/api-reference > Every route live under /v1/memory. Field names match the JSON the API sends and accepts. Base URL: `https://api.harny.ai/v1/memory`. Send `Authorization: Bearer $HARNY_APP_KEY` and `X-Harny-User: ` on every call, or a user key (`fsk_`) with no `X-Harny-User`. Every error body is `{code, status, debugMessage, data}` and every response carries `X-Request-Id`. [Errors](/docs/memory-api/errors) lists the codes. ### POST /v1/memory/ingest Adds one data point to a user's memory in an app. | Field | Type | Notes | |---|---|---| | `content` | string | Required. Not blank, no U+0000, at most 500,000 characters. | | `label` | string | At most 200 characters. | | `id` | string | No `/`, at most 512 characters. Defaults to the lowercase SHA-256 of `content`. | | `timestamp` | string | ISO 8601 with a time zone. Defaults to the stored value, else now. | | `metadata` | object | JSON object, at most 64 KiB serialized. | | `audience` | string | Omit it or send `project`. Other values return `audience-unknown`. | | `identity` | string | At most 200 characters. Must equal the acting user if sent. | | `idempotencyKey` | string | At most 200 characters. | Send the `X-Harny-User` header with an app key. A user key needs no `X-Harny-User`. An unknown field returns `invalid-ingestion`. Response `202`: `{projectId, ingestionId, sourceId, status: "queued", unchanged: null}`. `projectId` carries the app id. Response `200` with `status: "completed"` and `unchanged: true` when the stored data point is identical. Nothing is processed again in that case. Sending the same `id` with a change creates a new revision. Harny deletes the memories built on the old revision, then ingests the new text. Fields you leave out of the resend keep their stored values. If the new text is refused, the data point has no memories until you send it again, and that resend counts as a change. Error codes: `400` `content-required`, `id-invalid`, `nul-character`, `invalid-ingestion`, `audience-unknown`, `identity-required`; `401` `unauthorized`; `403` `identity-mismatch`; `409` `idempotency-conflict`, `data-point-deleted`, `data-point-processing`; `413` `content-too-large`; `429` `project-cap-reached` (or bodyless `429` for the per-minute limit); `503` `pipeline-disabled`, `organization-not-ready`. ```bash tab=cURL curl -X POST "https://api.harny.ai/v1/memory/ingest" \ -H "Authorization: Bearer $HARNY_APP_KEY" \ -H "X-Harny-User: user-123" \ -H "Content-Type: application/json" \ -d '{"content": "Met the client on Monday.", "label": "meeting note"}' ``` ```python tab=Python import os import requests resp = requests.post( "https://api.harny.ai/v1/memory/ingest", headers={ "Authorization": f"Bearer {os.environ['HARNY_APP_KEY']}", "X-Harny-User": "user-123", }, json={"content": "Met the client on Monday.", "label": "meeting note"}, ) print(resp.status_code, resp.json()) ``` ```typescript tab=TypeScript const resp = await fetch("https://api.harny.ai/v1/memory/ingest", { method: "POST", headers: { Authorization: `Bearer ${process.env.HARNY_APP_KEY}`, "X-Harny-User": "user-123", "Content-Type": "application/json", }, body: JSON.stringify({ content: "Met the client on Monday.", label: "meeting note" }), }); console.log(resp.status, await resp.json()); ``` ### POST /v1/memory/ingest/batch Adds up to 1,000 data points in one request. | Field | Type | Notes | |---|---|---| | `items` | array | 1 to 1,000 single-ingest objects, without `idempotencyKey`. An `id` may appear once per batch. | | `idempotencyKey` | string | At most 200 characters. | The whole batch is validated before any write. The batch is at most 16 MiB of UTF-8 in total. Each item is then written separately, so a quota refusal at item k keeps items before k. Response `202`: `{projectId, ingestionId, status: "queued", unchanged: null, items: [{ordinal, sourceId, status, unchanged}]}`. Response `200` with `completed` and `unchanged: true` when every item is unchanged. On a partial refusal, the error body includes `data: {ingestionId, accepted: k}`. Retry with the same `idempotencyKey` and body to resume at k. Error codes: `400` `invalid-ingestion` (empty, repeated id, or over 1,000 items), `413` `content-too-large`, plus the quota and availability codes from the single ingest route. ### GET /v1/memory/ingest/{ingestionId} Returns the processing status of one send. | Field | Type | Notes | |---|---|---| | `ingestionId` | path | The id returned by ingest. | Response: `{status, settled, blocked, readiness: {raw, comprehension, consolidation, failed}, failure}`. - `status` is `processing`, `failed`, or `completed`. - `readiness` counts the user's whole memory space, not one send. - `settled` is true when `raw`, `comprehension`, `consolidation` and `failed` are zero. - `blocked` is true when `raw`, `comprehension` and `consolidation` are zero and `failed` is above zero. - `failure` is null or `{code, message}`, where `code` is `content-rejected`, `model-unavailable`, `index-unavailable`, or `internal`. Error codes: `404` `not-found`. The status limiter applies. ### DELETE /v1/memory/ingest/{ingestionId} Deletes all data points that a send created. No request body. Response `204` with no body. Repeating the call also returns `204`. Error codes: `404` `not-found`; `409` `data-point-processing`, `memory-ids-unknown`. The memories the send created are deleted. If a send created none, existing memories are left in place. A data point still in the retry queue has its pending job removed. The id stays recorded as deleted, so sending it again returns `409 data-point-deleted`. ### DELETE /v1/memory/data-points/{sourceId} Deletes one data point and the memories built on it. This is the call that removes one piece of text you sent. To remove everything one send created, use `DELETE /v1/memory/ingest/{ingestionId}`. | Field | Type | Notes | |---|---|---| | `sourceId` | path | The data point's source id: the `id` you gave it when you sent it. | Response `202`: `{operationId, ids: [sourceId]}`. The delete has finished when the call returns. `operationId` identifies the delete. Error codes: `404` `data-point-not-found`; `409` `data-point-processing`, `memory-ids-unknown`. ### POST /v1/memory/data-points/delete Deletes up to 1,000 data points in one request. | Field | Type | Notes | |---|---|---| | `ids` | array | 1 to 1,000 source ids. A repeated id counts once. | Response `202`: `{operationId, ids}`. Nothing is deleted when any id is missing or any id is mid-delivery. Error codes: `400` `invalid-deletion`; `404` `data-point-not-found` with `data: {ids: [missing]}`; `409` `data-point-processing`, `memory-ids-unknown`. This route is free. ### POST /v1/memory/recall Returns the memories that match a query for one user. | Field | Type | Notes | |---|---|---| | `query` | string | Required. Error `recall-query-required` if missing. | | `identity` | string | Required. The user whose memory is read. A body `identity` must match the acting user, else `403 identity-mismatch`. | | `limit` | integer | 1 to 200. Default 100. | | `level` | string | `low` (8,000 tokens, default), `medium` (16,000), `high` (24,000), `extra-high` (32,000). Do not send with `maxTokens`. | | `maxTokens` | integer | Floor 256. Do not send with `level`. | | `queryTimestamp` | string | ISO 8601 with zone. Drops results whose `occurredAt` is later. Unknown times are kept. | | `occurredFrom` | string | Inclusive start of the `occurredAt` window. Unknown times are dropped. | | `occurredTo` | string | Inclusive end of the window. | | `sort` | string | `relevance` (default) or `chronological` (`occurredAt` ascending, unknown last). | Response `200`: ```json { "asOf": "", "usedEvidenceTokens": 0, "results": [ { "id": "", "rank": 1, "occurredAt": "", "content": "", "artifact": {"id": "", "kind": "observation", "occurredAt": ""}, "sources": [ {"sourceId": "", "occurredAt": "", "label": "