# Connect OpenAPI (any internal API) to Prodpeek Profile: `openapi/` · connection kind: `openapi` · tier 2 Credential: A read-only API key for your own API, or none Suggested URL: `https://orders.internal.example/api` Any internal API with an OpenAPI 3 spec. GET only. Reads that look like they return secrets are refused until you say otherwise. ## What you must not do You cannot create this credential on the user's behalf — it needs their login and, usually, an approval step. Walk them through it and verify the result. Never ask them to paste the credential into the chat; it goes straight into Prodpeek's console, which encrypts it at rest. Call add_openapi_service with the spec URL (or the document) and a service name, and hand the returned drop link to a human for the key. Never ask for the key in chat. If a read you need is refused as read_leaks, tell the human which one and why; only they can accept it, by editing the profile. ## Grant exactly these permissions - `The narrowest read-only key your API can issue` — Prodpeek only ever sends GET and HEAD, and refuses every other operation the spec describes. If your API can mint a key that cannot write on its own, use it: then two fences hold instead of one. - `No key at all, if the API has no authentication` — Connect it with the credential header set to `none`. Nothing is sent and there is no drop link. ## Refuse these, and say why if the user asks for them - `An admin or service-owner key` — The gateway refuses every write, but a key that can write is one bug in your API's own routing away from a write Prodpeek never saw as one. A GET handler that changes state is more common than it should be. - `A key sent as a query parameter` — The credential travels in a header you name. A key that only works in the URL ends up in your API's access logs, and Prodpeek does not put it there. ## Verify before the credential is used - Test connection lists your GET operations as allowed and every POST, PUT, PATCH and DELETE as refused. - Anything listed under 'these reads may return secrets' really does. Leave it refused unless you have checked. - The generated profile in data/profiles/local/openapi/.yaml says `verification.status: example`. ## Then, in Prodpeek 1. Services → Add a service → choose the profile `openapi/`. 2. Connection kind `openapi`, URL `https://orders.internal.example/api`. 3. Paste the credential. It is encrypted in the store and never shown again. 4. Run **Test connection**. It lists every tool the upstream advertises and how the profile classifies each one. Anything under "not in the policy" is denied by default — report that list rather than assuming it is fine. ## Full human walkthrough ## What it does Your spec already says what your API can do. Prodpeek reads it and turns every operation into a tool, then decides each one: - **GET or HEAD** becomes an allowed read, with the spec's own enums, required parameters and maximums. - **A parameter named `limit`, `per_page`, `page_size`, `count` or `top`** is capped at 200, or lower if the spec says so. - **A read that looks like it returns a secret** is refused as `read_leaks`, with the word named. The words are `token`, `secret`, `password`, `credential`, `api_key`, `private_key` and `connection_string`, looked for in the path, the operationId, the summary, the parameters and the response fields. - **A GET named like a change** (`logout`, `reset`, `purge`, `trigger`, `delete`) is refused as `write`, for you to look at. - **POST, PUT, PATCH and DELETE** are refused as `write`. There is no way to allow one. An argument the spec does not declare is refused too, rather than sent along. ## Step 1: connect it **In the console:** Services → **Connect an internal API from its OpenAPI spec**. Give it a name, the spec URL (or paste the document), and the header your API reads its key from. Leave the base URL empty to use the spec's `servers[0].url`. **From an agent:** ``` add_openapi_service name=orders spec_url=https://orders.internal.example/openapi.json auth_header=X-Api-Key ``` With no credential it returns a drop link for whoever holds the key. The spec is fetched without a credential. If yours sits behind authentication, paste it instead. ## Step 2: read what was generated The policy lands in `data/profiles/local/openapi/.yaml`, next to the spec it came from. It is yours to narrow: flip an `allow` to `deny`, lower a `maximum`. Prodpeek re-reads it on the next restart or refresh. To accept a read that was refused as `read_leaks` because you have checked it does not return a secret, set it to `allow`, give it `tier: 2`, and add `override: read_leaks_accepted`. The console lists every such override in amber, and an agent calling `prodpeek__explain` sees them too. A profile that allows a non-GET, or an operation the stored spec does not describe, or a `read_leaks` read without the override, is refused when it loads. The service then refuses every call until the file is fixed. ## Step 3: when the API changes Press **Refresh spec** (or call `refresh_spec`). Your edits are kept. Operations that disappeared are dropped. New reads arrive **refused**, noted `new since`, so nothing new is reachable before someone has looked at it. A read you allowed whose operation changed (it became a POST, or started returning a token) goes back to refused.