diff --git a/CHANGELOG.md b/CHANGELOG.md index dfc5b89..5a1feb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,12 @@ is cut by tagging the matching `vX.Y.Z` (see [Releasing](#releasing)). - Synced `ai-readiness`, `api-discovery` (search filters reference), and `bootstrap` skills to match their postmanlabs/postman-plugin source (whitespace/heading-level only, no behavior change). +- Synced `skills/` from postmanlabs/postman-plugin@4090901: `api-discovery` adds Orbit + public-API discovery (new `reference/orbit.md`) and drops `reference/search_filters.md`; + `bootstrap` now inspects the local and cloud workspace before choosing pull, push or init; + `collection-schema-v3` adds an environment schema reference; `api-mocking`, + `api-monitoring`, `api-testing`, `api-engineer`, `ai-readiness` and `postman-mcp-server` + get content updates. ## [1.3.1] - 2026-08-02 diff --git a/skills/api-discovery/SKILL.md b/skills/api-discovery/SKILL.md index 945a095..2f16efb 100644 --- a/skills/api-discovery/SKILL.md +++ b/skills/api-discovery/SKILL.md @@ -1,94 +1,135 @@ --- name: api-discovery -description: Finds what APIs, collections, specs, or requests already exist before building something new, and answers questions about how they relate. Use when the user asks "does an API for X already exist," "what depends on this service," "what does this API do," or before scaffolding anything that might already have a Postman equivalent. Covers `postman search`, `postman context-graph`, and `postman context instructions discovery`. Reach for `search` when you know roughly what you're looking for by name; reach for the Context Graph when the question is about relationships, not names. +description: Discover and use APIs from the web or Postman. Find and integrate public third-party APIs with Orbit, locate Postman entities with search, and use the Context Graph to investigate dependencies, ownership, runtime behavior, and change impact across an API ecosystem. --- # API Discovery ## Overview -Two unconnected datasets, so pick by data source, not question shape: - -- **`search` / `context`** → **Postman-authored artifacts** someone saved - in Postman (collections, requests, specs, mocks, workspaces). Matches - text; doesn't reason. "Does something named/shaped like X exist?" -- **`context-graph ask`** → a separately-populated **engineering service - graph** (built from repo/traffic scanning, not Postman content) — - natural-language Q&A over discovered services and the dependency edges - between them. "What depends on billing-api?" — architecture questions - `search` structurally can't answer, since there's no keyword for a - dependency edge. - -A miss in one says nothing about the other (see Critical Rule 1) — never -fall back to the Context Graph just because a `search` came back empty, or -vice versa. +Use this guide for any task that involves discovering an API — whether it +lives on the public web or inside Postman as a workspace, collection, request, +spec, mock, document, or flow. You can find entities across every surface: your +own private work, anything your team or organization shares, and resources +owned by external organizations. Beyond finding entities, this guide also +covers understanding how they relate to one another — for example, "which +services consume this API?" + +Each discovery option serves a distinct purpose: + +- **Orbit** → discovers and integrates **public third-party APIs**. No signup + or API key, and it uses ~27× less context than loading a vendor OpenAPI spec. + Search returns matching endpoints — including what each one + *cannot* do — and integrate returns a task brief specific enough to write + code against. It accepts both keyword and natural-language queries. Reach for + it instead of writing a third-party integration from memory. +- **`search`** → **finds any Postman entity**, for tasks like "update the tests + in my collection and run them" or "where is the documentation for our + access-control API?" +- **`context-graph ask`** → answers organization-wide relationship and impact + questions such as "what depends on billing-api?" or "what could this schema + change break?" + +These three draw on different data sources, so a miss in one is not proof of a +miss in the others. `search` locates a known Postman resource; the Context +Graph discovers relationships around a known starting point. Use both when a +task needs the resource itself and its wider impact. + +## Orbit — Public API Discovery + +Orbit finds public third-party APIs. It's free, needs no signup or API key, and +works entirely against publicly available APIs. REST base: +`https://api.buildwithorbit.ai`. Docs: `https://www.buildwithorbit.ai`. + +Reach for Orbit whenever a task needs an external capability — weather, +payments, invoicing, messaging, geocoding, calendar, and so on — even when the +user already named a provider. Rather than writing integration code from +memory, let Orbit hand you the details that matter: paths, auth header names, +required fields, and the rest. It works in two steps, search then integrate, +and a typical round trip runs ~2,500 tokens and ~15–20s end to end — against +~69,000 tokens for a full vendor OpenAPI spec. + +Two REST calls, both `POST`: + +1. **Search** (`POST /v1/search`) — describe the task, e.g. + `{ "q": "send email via SMTP" }`. Returns candidate endpoints, each with an + `id` and `resourceType` (pass both back verbatim) and an `evaluateGuide` + grading its fit. +2. **Integrate** (`POST /v1/integrate`) — send the task plus the chosen + resources (up to 10). Returns a `taskBrief` with `FIT`, `AUTH`, `BASE URL`, + `STEPS`, and `GOTCHAS` — read the GOTCHAS before writing the client. + +Full endpoint schemas, request/response shapes, `taskBrief` fields, and error +handling: [reference/orbit.md](reference/orbit.md). ## `search` -`search ` where type is `requests`, `collections`, -`workspaces`, `flows`, `specs`, `mocks`, `environments`, or `documents`. -Default `--ownership organization` only searches inside your org — pass -`external` or `all` before concluding "no API for this exists," since a -partner or public API published outside the org is invisible at the -default scope. `--filter "method=POST AND workspaceId=ws-123"` narrows -further; see [reference/search_filters.md](reference/search_filters.md) for -the full filter field and operator syntax per element type. - -## `context-graph` +`postman search ` finds any Postman entity, searching across +`requests`, `collections`, `workspaces`, `flows`, `specs`, `mocks`, +`environments`, or `documents`. The query can be a keyword or natural language, +and is optional (omit it to list or filter a type outright). Narrow with +`--ownership` and `--filter`, and add `-o json` for the enriched payload. An +empty default-scope result is not proof nothing exists — retry with +`--ownership all` before reporting that. -`context-graph ask "" --wait` is the one to reach for -interactively — it blocks and prints the answer. Without `--wait`, `ask` -returns an id immediately and `status ` checks on it later (exit -code 3 while still running) — useful for a question expected to take a -while, or from a script polling on its own cadence. The query runs against -the team derived from the API key; there's no workspace/team selection. -`--max-steps` caps how much reasoning the service does per question. +```bash +postman search requests "where do we validate a user's email?" +postman search collections "payments" --ownership external --filter "visibility=public" +``` -The answer is generated, not retrieved verbatim — verify with a re-ask or -narrower query before acting on it for anything consequential (Critical -Rule 3), the same way any AI-generated claim gets checked before it drives -a decision. +Use `postman search -h` for more details — ownership modes, the +`--filter` / `--filter-json` syntax, filter fields per type, and the exact +installed-version flags. -## `context instructions discovery` +## `context-graph` -Postman ships its own prescribed discovery workflow for AI coding agents — -`postman context instructions discovery` prints it. Read this before -building a custom discovery flow out of `search`/`context` primitives; it's -Postman's own recommended sequence (search/context only, no -`context-graph`), not a blank slate to reinvent. +The Context Graph is a private, authenticated map of an API ecosystem. It +reconciles Postman specifications, collections, monitors, and mocks; GitHub +repositories, definitions, and call sites; and New Relic deployments, traffic, +and telemetry. These become typed entities joined by sourced relationships such +as `calls`, `depends_on`, `owned_by`, and `monitored_by`. + +Use it before a cross-service or potentially breaking change. Name the endpoint, +schema, service, database, deployment, or shared module being changed; the graph +discovers the surrounding scope, including runtime callers and repositories not +checked out locally: + +```bash +postman context-graph ask "What depends on billing-api?" --wait +postman context-graph ask "What is the likely blast radius of changing this schema?" --wait +``` + +Treat the result as a lead, not proof. For consequential work, verify candidates +against source, API definitions, deployment configuration, or telemetry and +cite that evidence. Sources are connected through Postman's Agent Context UI +and refresh nightly; an unconnected or not-yet-ingested source makes absence +inconclusive. + +`--wait` polls the asynchronous API and prints the answer. Without it, `ask` +returns an ID for `postman context-graph status `. Use `--json` for the +structured record; `--timeout`, `--interval`, and `--max-steps` control waiting +and reasoning. + +The query runs against the team derived from the API key; there is no workspace +or team selector. Authentication uses `--api-key`, `POSTMAN_API_KEY`, or the +current `postman login` session, in that order. ## After discovery: reusing what was found `dependency add ` formally adds a collection, environment, or mock found in another workspace as a dependency of the current one — -the step after `search`/`context` finds something worth reusing (e.g., -feeding `application test`'s contract matching), rather than copying it in -by hand. It takes a Postman entity ID, so it only follows a `search`/ -`context` result — a `context-graph` finding names a service, not an ID; -go find that service's collection via `search` first. - -## Critical Rules - -1. **`context-graph` and `search`/`context` don't share a dataset** (see - Overview) — check an absence against its own source, never the other - tool, before reporting it to the user. -2. **An empty default-scope `search` is not proof nothing exists.** Retry - with `--ownership all` before reporting "no API for this" to the user. -3. **A Context Graph answer is generated reasoning, not a database read.** - Verify it against a concrete source before treating it as fact, - especially for anything the user will act on. -4. **`search`, `context-graph`, and `context` are Beta or recently added - surfaces.** Re-run `-h` before trusting a flag name here if the installed - CLI is newer than this file assumes — these are the commands most likely - to have changed since this was written. - -## Verification - -State which tool actually answered the question (search vs. Context Graph) -and at what `--ownership` scope or with what `--max-steps`/query — a -discovery answer is only as trustworthy as the scope it ran at. +the step after `search` finds something worth reusing (e.g., feeding +`application test`'s contract matching), rather than copying it in by hand. It +takes a Postman entity ID. If the Context Graph identifies a service or API to +reuse, locate its collection with `search` first, then pass that entity ID to +`dependency add`. ## Reference -- [Search filter syntax](reference/search_filters.md) — filter fields and - operators per element type, and `--filter-json` shape. +- [Orbit](reference/orbit.md) — public API discovery: the search/integrate + REST endpoints, request/response shape, `taskBrief` fields, and error + handling. (Docs at `https://www.buildwithorbit.ai`, REST at + `https://api.buildwithorbit.ai`.) + +For `postman search`, run `postman search -h` — the CLI's own help is +per-type, complete, and always matches your installed version. diff --git a/skills/api-discovery/reference/orbit.md b/skills/api-discovery/reference/orbit.md new file mode 100644 index 0000000..cc9e7da --- /dev/null +++ b/skills/api-discovery/reference/orbit.md @@ -0,0 +1,101 @@ +# Orbit — public API discovery reference + +Orbit finds and integrates **public third-party APIs** — weather, payments, +invoicing, messaging, geocoding, calendar, and the like. It is free, needs no +signup and no API key, and works entirely against publicly available APIs. + +- REST base: `https://api.buildwithorbit.ai` +- Docs: `https://www.buildwithorbit.ai` + +## Implementation + +Two REST calls, both `POST`, both read-only (safe to retry). No auth header — +send `Content-Type: application/json` and a JSON body. Run search first, +surface candidates, then integrate the chosen ids. + +```bash +# Step 1 — search +curl -sS https://api.buildwithorbit.ai/v1/search \ + -H 'Content-Type: application/json' \ + -d '{ "q": "send email via SMTP", "limit": 10 }' + +# Step 2 — integrate (ids come from the search response, verbatim) +curl -sS https://api.buildwithorbit.ai/v1/integrate \ + -H 'Content-Type: application/json' \ + -d '{ + "task": "Send a welcome email when a user signs up", + "resources": [{ "id": "urn:orbit:endpoint:v1:...", "type": "endpoint" }] + }' +``` + +Implementation notes: + +- A typical search+integrate is ~2,500 tokens and ~15–20s end to end, vs + ~69,000 tokens for loading a vendor OpenAPI spec. +- One integrate call can span up to 10 resources across different providers; + batch every endpoint the task needs into a single `resources` array rather + than making one call per endpoint. +- Auth, request bodies, `Threading`, and `GOTCHAS` come from live public API + schemas, so take them from the brief rather than from memory. + +## Step 1 — Search (`POST /v1/search`) + +Describe the task, not a provider name. Good: `"send an invoice to a customer"`. +Worse: `"PayPal"`. Provider name is fine to include when it is fixed. + +```json +{ "q": "send email via SMTP" } +``` + +Query params: + +- `limit` — default 10, max 25. +- `cursor` — from `meta.nextCursor`; pagination stops at 40 results. +- `q` — max 512 characters. + +Returns `data[]`. Each item has: + +- `id` — opaque URN. Pass it back verbatim; never construct, shorten, or edit + it. +- `resourceType` — `endpoint` or `mcp`. +- `name`, `method`, `url`, `description`. +- `evaluateGuide` — how well the endpoint fits the task, including what it + cannot do. + +Hold onto both `id` and `resourceType` — both are required for integrate. Do not +read `meta.total` as a match count; it reports the page size. + +## Step 2 — Integrate (`POST /v1/integrate`) + +Pass the same task plus every endpoint the job needs (up to 10). Use +`resourceType` from the search result as the `type` field. + +```json +{ + "task": "Send a welcome email when a user signs up", + "resources": [{ "id": "urn:orbit:endpoint:v1:...", "type": "endpoint" }] +} +``` + +Returns a `taskBrief` covering: + +- `FIT` — Fully or Partially (and names the gap if Partial). +- `AUTH` — use the exact header name given; it is frequently not + `Authorization`. +- `BASE URL`. +- numbered `STEPS` — method, path, every parameter with an example, expected + responses, `Threading`. +- `GOTCHAS` — read these before writing the client. + +## Error handling + +Both endpoints are read-only, so retries are safe. Free and unauthenticated is +not unlimited — back off on `429`. + +- `400` — invalid input. +- `404` on integrate — no IDs resolved. +- `500` — server error. + +If `FIT` is not Fully, say what's missing before writing code. If the brief +names a credential the user doesn't have yet, stop and tell them which one to +get. diff --git a/skills/api-discovery/reference/search_filters.md b/skills/api-discovery/reference/search_filters.md deleted file mode 100644 index c3443cf..0000000 --- a/skills/api-discovery/reference/search_filters.md +++ /dev/null @@ -1,84 +0,0 @@ -# `postman search` filter syntax - -Captured from `postman search -h` on the installed CLI. Re-run `-h` -to check for drift before trusting this on anything version-sensitive. - -## Global options (every `search `) - -- `--ownership ` — `organization` (or `org`, default), `external`, - `all`. -- `-n, --limit ` — max results per page, up to 25 (default 10). -- `--cursor ` — from a previous response, to paginate. -- `-o, --output ` — `default` (card list with a web link per - result) or `json` (full detailed response). -- `--filter ` — inline filter string. -- `--filter-json ` — filter as raw JSON; takes precedence over - `--filter` if both are given. - -## `--filter` expression syntax - -- `=` / `!=` for a single value: `workspaceId=ws-1` -- `IN` / `NIN` for comma-separated values: `workspaceId IN ws-1,ws-2` -- Combine multiple conditions with `AND` (case-insensitive operators). -- Quote a value containing spaces or commas: `name="value with spaces"` - -## `--filter-json` shape - -Only a top-level `$and` array of condition objects — no `$or`, no nested -groups: - -```json -{"$and":[{"workspaceId":{"$eq":"ws-abc123"}}]} -``` - -Each condition maps one field to one operator object: `$eq`, `$ne`, `$in`, -`$nin`. - -## Fields shared across all element types - -| Field | Operators | Value | -| --- | --- | --- | -| `privateNetwork` | `$eq`, `$ne` | boolean | -| `publisherIsVerified` | `$eq`, `$ne` | boolean | -| `visibility` | `$eq`, `$ne` | `internal` \| `public` \| `partner` | -| `workspaceId` | `$eq`, `$ne`, `$in`, `$nin` | id | -| `createdBy` | `$eq`, `$ne`, `$in`, `$nin` | user id | -| `organizationId` | `$eq`, `$ne`, `$in`, `$nin` | org id | -| `teamId` | `$eq`, `$ne`, `$in`, `$nin` | team id | -| `isGitConnected` | `$eq`, `$ne` | boolean | - -ID filters expect actual IDs — run a broader search with `--output json` -first to find them. - -## Element-specific fields - -**`requests`** - -| Field | Operators | Value | -| --- | --- | --- | -| `collectionId` | `$eq`, `$ne`, `$in`, `$nin` | id | -| `requestId` | `$eq`, `$ne`, `$in`, `$nin` | id | -| `method` | `$eq`, `$ne`, `$in`, `$nin` | `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, ... | -| `type` | `$eq`, `$ne`, `$in`, `$nin` | `http-request`, `graphql-request`, `grpc-request`, `ws-raw-request`, `ws-socketio-request`, `mqtt-request`, `llm-request` | - -**`specs`** - -| Field | Operators | Value | -| --- | --- | --- | -| `specificationId` | `$eq`, `$ne`, `$in`, `$nin` | id | - -Run `postman search --help` for the exhaustive per-type list — this -file covers `requests` and `specs`; the others (`collections`, -`workspaces`, `flows`, `mocks`, `environments`, `documents`) share the -common fields above plus their own, not yet captured here. - -## Examples - -```bash -postman search requests "login" -postman search requests "auth" --filter "method=POST AND collectionId=collection-uid-1" -postman search requests "upload" --filter "method IN POST,PUT AND workspaceId=ws-abc123" -postman search requests "payment" --ownership external --limit 5 --filter "visibility=public" -postman search specs "billing" --ownership external -postman search specs "payments" --filter "publisherIsVerified=true AND visibility=public" -``` diff --git a/skills/api-engineer/SKILL.md b/skills/api-engineer/SKILL.md index 6d1400f..1add115 100644 --- a/skills/api-engineer/SKILL.md +++ b/skills/api-engineer/SKILL.md @@ -14,6 +14,14 @@ description: Default entry point for API engineering work — designing, impleme 6. Don't push to the cloud workspace (`postman workspace push`) without user consent. The recommended way to push to the cloud is a CI step on PR merge - see **ci-integration**. 7. For high-quality API search results, use **api-discovery**. 8. No is an acceptable answer. Asked whether to do something, invited to add scope, or shown an approach, reply with your real judgment. +9. Prefer filesystem-first Postman workflows. For an existing cloud workspace, + `postman workspace pull ` connects it and materializes its collections, + environments, and specs locally. When no cloud workspace exists, `postman + init --no-cloud` initializes the local structure. Work against those files, + validate them, and push only with user consent; sharing an already-bound + workspace means `workspace push`, not creating a duplicate. See + **bootstrap** for the lifecycle decision table. +10. When actual use exposes a concrete Postman CLI gap or a misleading skill, handle the user's task first — then use `postman feedback` to report the gaps/bugs. Exclude secrets, user data, and proprietary content ## Dos 1. Prove it works - validate the task against the contract. See **api-testing**. diff --git a/skills/api-mocking/SKILL.md b/skills/api-mocking/SKILL.md index d29870c..5efa5f9 100644 --- a/skills/api-mocking/SKILL.md +++ b/skills/api-mocking/SKILL.md @@ -7,113 +7,135 @@ description: Stands up a fake backend that behaves like a real API — from a co ## Overview -A mock is two files on disk: `config.yaml` (name, port, list of scenarios) -and `default.js` — a plain Node HTTP server, and the mock itself, not a -wrapper around one. Because it's just code, generating, inspecting, running, -and calling a mock are all local and work for a logged-out guest. Only -sharing it — pushing it to the cloud and deploying a durable URL — needs -`postman login`. +This skill covers the Postman CLI (`postman mock`) for **Code Mocks** — the +code-based mock product. It is not the postman-app UI (Local Mode sidebar, +Agent Mode tools, Simulations), nor the older classic/collection mocks that +serve saved collection examples from a `*.mock.pstmn.io` URL — that's a +different product (the MCP `createMock` flow), not this skill. -Local and cloud aren't a choice made once at `generate` time. Every mock -starts as a local folder; `mock push` promotes any existing local folder to -the cloud later, whether or not `-w` was passed at generate. Reach for cloud -only when something other than you needs to hit this mock over the network — -a teammate, a CI job elsewhere, a webhook sender. A purely local mock -answering a `postman request` on your machine never needs it. +A **local** mock is two files on disk: `config.yaml` (name, port, scenarios) and +`default.js` — a plain Node HTTP server, and the mock itself, not a wrapper +around one. Generating, inspecting, running, and calling a local mock work for +a logged-out guest. Only sharing it — pushing to the cloud and deploying a +durable URL — needs `postman login`. The progression is one model in three +places: local folder ──`push`──▶ Code Mock (cloud definition) ──`deploy`──▶ +Mock Server (the reachable URL). + +Default path: write a local folder, then `mock push` later if something other +than you needs to hit it over the network (a teammate, CI elsewhere, a webhook +sender). A purely local mock answering a `postman request` on your machine +never needs cloud. The exception is `generate -w`, which creates the mock in a +workspace only and writes no local files — use that only when you intentionally +skip the repo copy. ## Process -1. **Generate.** `postman mock generate -n NAME --port N` with no source - scaffolds a complete sample cart mock — the fastest way to a server that - already answers, useful whenever the point is exercising mock *behavior* - rather than a specific API's shape. Pass a real source — - `postman mock generate SOURCE -n NAME --port N`, where `SOURCE` is a - collection folder or an `openapi.yaml` — when the endpoints need to mirror - an actual API. Either form writes `config.yaml` + `default.js` into - `postman/mocks/NAME/`. Adding `-w ` also creates the mock in - that workspace on top of writing the local files — it doesn't replace the - local write, and it requires being logged in. -2. **Run it.** `postman mock run ./postman/mocks/NAME` starts - `Mock server started at http://localhost:PORT`. If the port in - `config.yaml` is taken and `--port` wasn't passed explicitly, it falls - back to a free OS-assigned port instead of erroring — read the real port - off that line rather than assuming the configured one. Naming `--port` - explicitly makes a taken port a hard `Port already in use` error instead. +1. **Generate.** `postman mock generate -n NAME` with no source scaffolds a + sample shopping-cart mock (`POST /cart/items`, `GET /cart`, `POST /checkout`) + — the fastest way to a server that already answers, useful whenever the + point is exercising mock *behavior* rather than a specific API's shape. Pass + a real source — `postman mock generate SOURCE -n NAME`, where `SOURCE` is a + collection file/directory or an `openapi.yaml` — when the endpoints need to + mirror an actual API. Either form writes `config.yaml` + `default.js` into + `postman/mocks/NAME/` (default port 4500). + - `postman mock generate SOURCE --update ./postman/mocks/NAME` regenerates + the default handler from the source in place, keeping the existing name, + port, and scenarios. `--update` still needs the `SOURCE`; it cannot be + combined with `--output`. + - `-w ` saves the mock to a cloud workspace *instead of* the + repository — it writes no local files and requires being logged in. Cannot + be combined with `--output`, `--force`, or `--update`. Prefer local + generate + later `push` when you still need `mock run` from a folder. +2. **Run it.** `postman mock run ./postman/mocks/NAME` starts the server and + prints the bound URL (`... at http://localhost:PORT`). If the port in + `config.yaml` is taken and `--port` wasn't passed explicitly, it falls back + to a free OS-assigned port instead of erroring — read the real port off that + line rather than assuming the configured one. Naming `--port N` explicitly + makes a taken port a hard error; `--port auto` always picks a free one. 3. **Call it.** Plain `postman request localhost:PORT/route` returns the - default scenario's response. Two headers change that per-request, with no - restart needed: `x-mock-scenario: ` selects a different scenario - (the valid names live in `config.yaml` — `mock get` only ever shows the - default one), and `x-mock-response-code: ` returns that status - instead. A wrong route and a wrong scenario name both come back as - `Endpoint not defined` — indistinguishable from the message alone. - There's no hot reload: a `default.js` edit does nothing until you - Ctrl+C the running server and `mock run` it again. + default scenario's response. Two headers can change that per-request, with no + restart: `x-mock-scenario: ` selects a scenario the mock defines + (valid names live in `config.yaml`); a name the mock doesn't define is not an + error — it falls back to the default scenario. `x-mock-response-code: ` + filters an endpoint's saved example responses to the one with that status, so + it only changes anything when that endpoint actually has an example for that + code — mocks generated from a collection/spec with multiple example statuses + honor it; the built-in sample mock has one response per route and ignores it. + A wrong route comes back as `Endpoint not defined`. There's no hot reload: a + `default.js` edit does nothing until you Ctrl+C the running server and + `mock run` it again. 4. **Push it, if it needs to leave your machine.** - `postman mock push ./postman/mocks/NAME` is safe to re-run — `Created` - the first time, `Updated` after. It returns a cloud ID that is a *new* - identifier, not the `id` already sitting in `config.yaml` — see - [The three identifiers](#the-three-identifiers) below. It also modifies - `.postman/resources.yaml`; commit that change. + `postman mock push ./postman/mocks/NAME` is safe to re-run — `Created` the + first time, `Updated` after — and records the cloud mapping in + `.postman/resources.yaml`; commit that change. If a mock server is already + live for this mock, the push updates what it serves. 5. **Deploy it, for a URL that outlives your terminal.** - `postman mock deploy CLOUD_ID -s SLUG -y --auto-deploy` prints - `https://SLUG.mock..postman.dev`. Deployed private by - default — callers need an API key — add `--public` only when the mock - should be reachable by anyone with the URL. `-y` alone accepts - private/auto-deploy-off; without `--auto-deploy`, a later `push` doesn't - change what's actually being served until you `deploy` again. -6. **See who's calling it.** `postman mock get CLOUD_ID --json` returns - `.mockServerId`; feed that into `postman mock log MOCK_SERVER_ID --json` - for call entries. An empty log means the URL genuinely hasn't been hit — - a rejected caller still shows up, recorded with its failing status code. -7. **Tear down.** `postman mock delete ./path --yes` (local) or - `postman mock delete CLOUD_ID --yes` (cloud) — both refuse while the - thing is still alive, so stop the local run or bring the deployment down - first. Cloud delete doesn't touch `.postman/resources.yaml`; drop that - line by hand afterward or the repo keeps claiming a mock that's gone. + `postman mock deploy CLOUD_ID -s SLUG -y` prints + `https://SLUG.mock..postman.dev`. Deployed private by default — + callers need a Postman API key (`x-api-key`) — add `--public` only when the + mock should be reachable by anyone with the URL. `--auto-deploy` re-publishes + the live server automatically whenever the mock changes; even without it, a + later `push` already updates a live server, so you only re-`deploy` for the + first URL or after taking the server down. +6. **See who's calling it.** `postman mock get CLOUD_ID` (table or `--json`) + returns `mockServerId`; feed that into `postman mock log MOCK_SERVER_ID` for + call entries (filter with `--method` / `--status` / `--path` / `--since` / + `--until` / `--limit`, or `--json`). An empty log means the URL genuinely + hasn't been hit — a rejected caller still shows up, recorded with its failing + status code. +7. **Tear down.** + - Local: `postman mock delete ./path --yes` — refuses while that mock is + **running** locally; stop `mock run` first. + - Cloud: `postman mock delete CLOUD_ID --yes` — refuses while the mock is + **running locally** *or* **deployed**; stop the local run and take the + mock server down first. + Cloud delete doesn't touch `.postman/resources.yaml`; drop that line by hand + afterward or the repo keeps claiming a mock that's gone. To point real request/assertion runs at a mock instead of hand-editing -base-URL variables, see the `api-testing` skill's `--use-mock`/`--mock` -flags on `collection run`. +base-URL variables, see the `api-testing` skill's `--use-mock`/`--mock` flags +on `collection run`. -## The three identifiers +## The two ids that matter -`config.yaml`'s local `id`, the cloud ID `push` returns, and the -`mockServerId` from `get CLOUD_ID --json` are three different values, in the -order they become available. Only the second and third are lookups — -`push` is a creation, not a promotion, so passing the `config.yaml` id to any -cloud command is passing the wrong key, not a stale one. +- **Code-mock id** — the `id` in `config.yaml`, and the id that `push` and + `generate -w` print (often the same value). Use it for `get`, `deploy`, + `delete`, and `run` by cloud id. +- **`mockServerId`** — a different value from `mock get CLOUD_ID` (table or + `--json`). Use it for `mock log`, and nothing else. ## Critical Rules -1. **Every gated cloud command fails the same way:** - `Authentication required. Run postman login or provide --api-key`, - exit 1, nothing half-done. Whether a command is gated is decided by what - you pass it, not the verb — `mock get`/`mock run` take either a local - path (ungated) or a cloud ID (gated); `mock list` is gated only when - called with no path. +1. **When you're not signed in, every gated cloud command fails closed:** + `Authentication required. Run postman login or provide --api-key`, exit 1, + nothing half-done. (Signed in but lacking access fails differently — a + permission or missing-workspace error.) Whether a command is gated is decided + by what you pass it, not the verb — `mock get`/`mock run` take either a local + path (ungated) or a cloud ID (gated); `mock list` is gated only when called + with no path. 2. **`push` is what moves an existing local mock to the cloud — `-w` at - `generate` time is optional, not a fork you must choose up front.** A - mock built as a guest can be pushed and deployed later with no rework. + `generate` time is optional, not a fork you must choose up front.** A mock + built as a guest can be pushed and deployed later with no rework. Do not + assume `generate -w` left a `postman/mocks/NAME/` folder to `run`. 3. **`--public` on `deploy` is the one action here with real exposure** — it - stands up a server anyone with the URL can hit, with no API key. The - default (private) is the safe one; confirm intent before adding it. -4. **Don't reuse the `config.yaml` id for a cloud command, and don't invent - a `mock generate` flag for regenerating in place.** The reference for - this CLI documents editing `default.js` and restarting as the way to - change a running mock's behavior — no in-place "update from source" verb - is documented. Run `postman mock generate -h` before assuming one exists - rather than guessing a flag name. + stands up a server anyone with the URL can hit, with no API key. The default + (private) is the safe one; confirm intent before adding it. +4. **To change a mock:** refresh it from a source with + `postman mock generate SOURCE --update PATH`, or edit `default.js` by hand + and restart `mock run`. Never pass a code-mock id to `mock log` — that + command takes a `mockServerId`. 5. **`-w`/`--workspace` only exists on `generate`, `list`, `push`, and - `deploy`.** `get`, `run`, `log`, and `delete` already take a path or an - id that says where the mock is — there's nothing left for `-w` to - resolve on those. + `deploy`.** `get`, `run`, `log`, and `delete` already take a path or an id + that says where the mock is — there's nothing left for `-w` to resolve on + those. ## Verification A mock isn't done because `generate` or `run` exited 0 — hit it with `postman request` and check the actual status/body, or `mock get CLOUD_ID --json` for a cloud one, then state whether it ended up local or cloud, and -(if deployed) private or public. For a scenario/status-code check, confirm -the header actually changed the response — a typo'd scenario name returns the same -`Endpoint not defined` as a wrong route, so a passing exit code alone proves -nothing. +(if deployed) private or public. For a scenario check, confirm a *valid* name +from `config.yaml` actually changed the response — a typo'd name falls back to +the default, so a 200 alone proves nothing. For a status-code check, use an +endpoint that has an example for that code (not the sample mock). A passing +exit code from `request` is not enough. diff --git a/skills/api-monitoring/SKILL.md b/skills/api-monitoring/SKILL.md index c52d82c..e651639 100644 --- a/skills/api-monitoring/SKILL.md +++ b/skills/api-monitoring/SKILL.md @@ -83,12 +83,19 @@ assertions ran during one attempt, which failed, and why. Reach for these instead of re-running blind after a `-t` timeout, or whenever the task is explaining *why* a monitor failed rather than just that it did. -## Self-hosted runners +## Private (self-hosted) runners + +A private runner — the CLI's `runner regions` calls the same thing a +"private-runner" value — is an agent you run inside your own network so +Monitor traffic originates there instead of from Postman's cloud IPs. Reach +for one only when the monitored API sits behind a VPN, firewall, or on-prem +network that Postman's cloud can't reach directly; a public API should just +use a Postman region (`--runner us-east`, etc.) since that needs no +infrastructure of your own to run or maintain. `runner start --id --key ` (from the Postman app) registers a runner that executes monitor runs from your own infrastructure instead of -Postman's cloud. This matters only when the monitored API isn't reachable -from the public internet — a public API needs no self-hosted runner. Extra +Postman's cloud. Extra flags cover the runner's own networking: `--region eu` for EU residency, `--proxy`/`--egress-proxy`/`--egress-proxy-authz-url` for outbound routing, `--ssl-extra-ca-certs` for a private CA, and `--metrics`/`--metrics-port` @@ -102,27 +109,21 @@ string. ## Critical Rules -1. **Monitor creation and scheduling are real CLI tasks now.** Don't send - the user to the Postman app for this — `monitor create` with `-c` and - `--schedule` does it. Notifications here are email only - (`--notify-email`); a request for a different alert channel (Slack, a - webhook) is the one piece still worth checking against the app rather - than assuming a flag exists. -2. **`update` can't move a monitor to a different collection or environment, +1. **`update` can't move a monitor to a different collection or environment, and doesn't pause/resume it.** Delete and recreate for the former; use `pause`/`resume` for the latter. -3. **A `monitor run -t` timeout is a wait cap, not a monitor failure.** +2. **A `monitor run -t` timeout is a wait cap, not a monitor failure.** Don't report "the monitor failed" from a timeout without checking `monitor jobs get`/`monitor runs get` (or the Postman app) for what the run actually did after the CLI gave up waiting — or avoid the wait entirely with `--async`. -4. **`--runner` on `monitor create`/`update` takes a region name or a +3. **`--runner` on `monitor create`/`update` takes a region name or a self-hosted runner id — check `runner regions`/`runner list` before guessing a string,** and don't suggest `runner start` unless the target API genuinely isn't reachable from Postman's cloud. -5. **`monitor list --offset` doesn't work — use `--cursor`,** and +4. **`monitor list --offset` doesn't work — use `--cursor`,** and `--runner` there can't be combined with the other filters. -6. **`monitor delete` is permanent.** Confirm intent before passing `-y` to +5. **`monitor delete` is permanent.** Confirm intent before passing `-y` to skip its prompt. ## Verification diff --git a/skills/api-testing/SKILL.md b/skills/api-testing/SKILL.md index 42c0796..fd9a98a 100644 --- a/skills/api-testing/SKILL.md +++ b/skills/api-testing/SKILL.md @@ -11,7 +11,8 @@ Three tools, matched to what already exists: | Have | Use | | --- | --- | -| Just a URL to check | `postman request` | +| Just a URL to check, with no saved request | `postman request` | +| A request already saved in a collection | `postman collection run -i ` | | A collection with `pm.test` assertions saved in it | `postman collection run` | | A real app (browser flow, CLI, service) whose traffic should match a collection's contract | `postman application test` | @@ -31,6 +32,21 @@ failing test still exits nonzero. Useful for a quick check or a CI health check; not the place to accumulate assertions that should outlive one command — those belong saved in a collection. +Before constructing a URL, headers, auth, and body on the command line, look +for a matching `*.request.yaml` under `postman/collections/`. If it exists, +execute the saved request through its collection: + +```bash +postman collection run "postman/collections/Orders API" -i "create order" +``` + +Do not copy the saved YAML fields into `postman request`; that bypasses the +collection's inherited variables, auth, scripts, and maintained payload. The +`postman request` positional target is a URL, not a `.request.yaml` path. When +there is no saved request but its body already lives in a separate file, keep +the file as the source of truth with `--body @path/to/payload.json` instead of +inlining its contents. + ## `collection run` — the assertion suite Debugging a request's behavior during a run (why a `pm.test` failed, what a @@ -75,6 +91,9 @@ that shouldn't be recorded. the request instead. 4. **`--use-mock` is the way to test without a live backend** — prefer it over standing up ad hoc fakes or skipping tests that need a dependency. +5. **Reuse a saved request instead of reconstructing it.** If a matching v3 + request exists, use `collection run -i `; reserve + `postman request` for genuinely ad-hoc requests. ## Verification diff --git a/skills/bootstrap/SKILL.md b/skills/bootstrap/SKILL.md index e22bb4f..ec00ee3 100644 --- a/skills/bootstrap/SKILL.md +++ b/skills/bootstrap/SKILL.md @@ -1,6 +1,6 @@ --- name: bootstrap -description: Resolves the Postman CLI, authenticates, links the workspace, and records this repo's spec path, collections directory and workspace id. Use when the user asks to "set up Postman here", "connect this repo to Postman", "link this workspace", "authenticate with Postman", "postman login", or "run postman init" — and before the api-mocking, api-testing, api-monitoring, flows, performance-testing, api-discovery, or ci-integration skills only when the CLI, the linked workspace or the spec path has not already been confirmed in this session. Those skills stop and point back here if it has not completed; they never re-derive these values themselves. +description: Resolves the Postman CLI, authenticates when the task needs it, and manages the filesystem/workspace binding for a repository. Use when the user asks to set up Postman, enable filesystem workflows, authenticate, initialize, import, connect, pull, push, sync, or share a workspace — and before skills that need a linked workspace, only when the CLI, linked workspace, or spec path has not already been confirmed. --- # Bootstrap Postman for This Repo @@ -14,14 +14,16 @@ repo is already set up. ## Rules -- Make HTTP calls with `postman request`, never `curl` or another client — it - reuses saved auth and env vars, syncs to the workspace, and runs test - assertions. +- Make ad-hoc HTTP calls with `postman request`, never `curl` or another + client. If the request already exists in a collection, preserve its saved + auth, variables, scripts, and payload by using `postman collection run + -i ` instead of reconstructing it on the command + line; see `api-testing`. - Never invent a subcommand or a flag. Run `-h` first and believe it. - Lint specs with `postman spec lint`, never `postman api …` — the API Builder is deprecated in v12+ and the CLI prints no warning. -- Local commands need no login; only commands reaching the Postman cloud do. - Don't force a login the task doesn't need. +- Local commands need no login; only commands that reach the Postman + workspace do. Don't force a login the task doesn't need. - A missing `postman` binary means install it. Route to `postman-mcp-server` only after an install has been attempted and actually failed. - Never fabricate a workspace id, spec path, or collections directory. Report @@ -30,8 +32,10 @@ repo is already set up. - "Present" is not "current": check the version and existing links before setting anything up. - Wire up an existing repo only. Never scaffold a new API or a starter spec. -- Write no host-specific paths — one `skills/` directory loads on Claude Code, - Cursor and Kimi Code. +- Write no host-specific paths — the same `skills/` directory loads on every + route. +- Do not use `init` or `workspace create` to share or import a workspace that + already exists. Choose the direction of sync from the lifecycle table below. ## Ask the CLI: `-h` @@ -54,7 +58,7 @@ pick from what it prints. ## Process -Four steps, in order. Stop at the first that fails and report which one. +Three steps, in order. Stop at the first that fails and report which one. ## 1. Resolve the CLI @@ -93,15 +97,14 @@ access, or a hosted session that cannot install — then hand off to the `postman-mcp-server` skill. An attempted install that actually failed is the only thing that qualifies. -## 2. Authenticate, if the task needs it +## 2. Establish the filesystem and workspace bindings -### 2.1 Decide whether auth is required +### 2.1 Authenticate only if this step needs it Local commands need no login, and `postman init` is among them — its own help -says *"No authentication, and safe in CI."* Skip to step 3 unless something in -the task reaches the Postman cloud. - -### 2.2 Sign in +says *"No authentication, and safe in CI."* Skip this entirely unless the +command you're about to run pulls or pushes an existing workspace, or shares +one with a team. **With an API key — preferred, non-interactive:** @@ -119,14 +122,40 @@ postman login skill writes no credential file. Report that authentication succeeded, nothing more. -## 3. Record the bindings - -### 3.1 Check for an existing record - -Read `.postman/resources.yaml` for `localResources` and `workspace.id`. -Populated → go to step 4. Absent or empty → run init. - -### 3.2 Run init +### 2.2 Inspect both sides before choosing a command + +Read `.postman/resources.yaml` for `localResources` and `workspace.id`, and +inspect the local `postman/` tree. When the user names an existing workspace or +asks to import, sync, or share one, use `workspace list --json` and `workspace +get --elements --json` to confirm the workspace side. Never create a +second workspace merely because this repository is not connected yet. + +Prefer filesystem-first work: materialize an existing workspace with +`workspace pull `, or initialize local files with `postman init --no-cloud` +when no workspace exists. Then inspect, edit, diff, and validate the +version-controlled files before any push. + +| Existing state and intent | Use | Why | +| --- | --- | --- | +| No workspace exists; start locally | `postman init --json --no-cloud` | Creates the git-native filesystem without requiring login. | +| No workspace exists; create and bind one | `postman workspace create --visibility ` or the explicit init creation path | Creation is the requested lifecycle event. | +| Workspace exists; enable filesystem work | `postman workspace pull ` | Connects the workspace to the repository and materializes its entities under `postman/`. | +| Workspace exists; record only the Git binding | `postman workspace connect-git [path]` | Binds without downloading its contents. | +| Bound workspace; the workspace is authoritative | `postman workspace pull` | Refreshes local files from the connected workspace. | +| Bound workspace; local files are authoritative | `postman workspace diff --push-strategy default`, then `postman workspace push` | Previews and publishes creates/updates without deleting unmatched workspace entities. | +| “Share this existing workspace with my team” and it is already team-accessible | Diff, then `postman workspace push` | Publishes local contents to the existing workspace; `create` would make a duplicate. | + +If “share” also requires changing a personal workspace's visibility or team +permissions, inspect its metadata first. `push` synchronizes entities; it does +not change access control. Do not create a replacement to work around a missing +metadata-update command. + +`workspace diff` is read-only. Match its push strategy to the intended push. +`--push-strategy force-sync` can delete workspace entities absent locally, so use it +only when the user explicitly requests mirroring and approves the shown +deletions. Do not add `-y` merely to bypass a prompt. + +### 2.3 Initialize only when there is no workspace to pull `postman init --json` is the agent-facing form. It writes `.postman/resources.yaml` and scaffolds `postman/` for specs, collections and @@ -137,6 +166,10 @@ postman init --json --no-cloud # local only, no workspace postman init --json --visibility personal # also create and bind a workspace ``` +Use `--visibility` only when a new workspace is actually wanted. If the +workspace already exists, use `pull` to enable the filesystem workflow; +use `push` only when publishing local changes to an already-bound workspace. + **The workspace step is interactive** without `--no-cloud` or `--visibility`. **Read the payload, not stderr.** Take `bindings` and `exitCode` from the JSON. @@ -147,19 +180,22 @@ guess worth confirming before building on it. authoritative, so re-run with `--spec `. 5 means the local files were written but the requested workspace was not created — it does *not* mean re-run. -## 4. Verify and report +## 3. Verify and report -### 4.1 Checkpoints +### 3.1 Checkpoints - `postman --version` returned a real version. - Auth is confirmed, or established as not required for this task. - `.postman/resources.yaml` names a spec or a collections directory. - `workspace.id` is set, or the run was deliberately local-only — `--no-cloud` leaves it empty and still exits 0, which is a pass, not a gap. +- After `pull`, expected workspace entities exist under `postman/`. After + `push`, report created/updated entities and conflicts; do not claim a + workspace is shared unless its access level permits the intended teammates. "The CLI is installed" is not the bar, and a loaded skill configures nothing. -### 4.2 Summary format +### 3.2 Summary format ```md ## Postman bootstrap diff --git a/skills/collection-schema-v3/SKILL.md b/skills/collection-schema-v3/SKILL.md index a9fcbe4..a2021fe 100644 --- a/skills/collection-schema-v3/SKILL.md +++ b/skills/collection-schema-v3/SKILL.md @@ -96,12 +96,12 @@ Saved examples are what `collection ai-readiness` checks for — a request with no examples scores worse for agent consumption even if perfectly valid structurally. -## Environments (`postman/environments/*.yaml`) +## Environments (`postman/environments/*.environment.yaml`) -- `name` — required. -- `values` — array of `{key, value: string, enabled, type}`. `value` must - be a string (same rule as collection variables); `enabled` is boolean; - `type` is a string, e.g. `"default"`. +Environment files are v3 YAML but are not collection entities: they do not use +`$kind`. Before creating or editing one by hand, read +[reference/environment.md](reference/environment.md) for the schema, secret +handling, CLI-first edit commands, and a linted example. ## YAML rules @@ -206,3 +206,5 @@ variables: - [Other request protocols](reference/other_protocols.md) — GraphQL, gRPC, WebSocket, Socket.IO, MQTT, MCP, and LLM request schemas. +- [Environment schema](reference/environment.md) — v3 environment filenames, + fields, variable types, safe editing commands, and validation. diff --git a/skills/collection-schema-v3/reference/environment.md b/skills/collection-schema-v3/reference/environment.md new file mode 100644 index 0000000..d057907 --- /dev/null +++ b/skills/collection-schema-v3/reference/environment.md @@ -0,0 +1,63 @@ +# Environment Schema (v3) + +Read this reference before creating, editing, or debugging files under +`postman/environments/`. + +## Prefer the CLI for ordinary edits + +The CLI preserves the schema and avoids leaking secret values into command +output: + +```bash +postman environment new "Staging EU" +postman environment var set baseUrl https://staging.example.com \ + --environment "postman/environments/Staging EU.environment.yaml" +postman environment var unset oldToken \ + --environment "postman/environments/Staging EU.environment.yaml" +postman environment lint postman/environments --fail-severity warning +``` + +Use `environment get --show-secrets` only when the user explicitly needs the +secret value revealed. Do not print, summarize, or commit credentials returned +by it. + +## File and fields + +An environment is one YAML file named `.environment.yaml` under +`postman/environments/`. It has no `$kind` field. + +- `name` — required string. +- `values` — required array; an empty environment uses `values: []`. +- Each value has: + - `key` — required variable name. + - `value` — required string. Quote booleans, numbers, empty values, and + values containing YAML punctuation or `{{variables}}` so YAML does not + coerce them. + - `enabled` — boolean. The CLI writes `true` for a newly set variable. + - `type` — optional string. Use `default` for ordinary values and `secret` + for sensitive values. + - `description` — optional string. + +Do not add collection-only fields such as `$kind`, `scripts`, `auth`, or +`variables`. Do not put secrets into an example merely to make it executable; +leave the value empty or use the team's supported secret source. + +## Linted example + +```yaml +name: Staging EU +values: + - key: baseUrl + value: 'https://staging.example.com' + enabled: true + type: default + description: API base URL + - key: apiToken + value: '' + enabled: false + type: secret +``` + +After any hand edit, run `postman environment lint `. Use +`postman workspace lint` when the task is to validate the entire local +workspace, not just its environments. diff --git a/skills/postman-mcp-server/references/learn.md b/skills/postman-mcp-server/references/learn.md index b8f9e89..b35e2d6 100644 --- a/skills/postman-mcp-server/references/learn.md +++ b/skills/postman-mcp-server/references/learn.md @@ -11,10 +11,14 @@ Use this to learn *about Postman itself* — not to search the user's own collec ## Prerequisites -This command uses `searchLearningCenter`, which is only exposed when the Postman MCP Server runs in **Full mode** (`POSTMAN_MCP_MODE=mcp`, the default). It is **not** available in `minimal` or `code` mode. +This command uses `searchLearningCenter`, which the Postman MCP Server exposes only in **Full mode**. + +The mode is fixed by the endpoint in each route's MCP config, not by the environment. A route pinned to `https://mcp.postman.com/mcp` has the tool; a route pinned to `https://mcp.postman.com/minimal` does not. Read it off the route's own MCP config rather than inferring it from the agent's name — which endpoint an agent gets is a per-route product decision, and new routes are added. **`POSTMAN_MCP_MODE` is not read on any route** — never tell the user to set or unset it to change the tool set. - If MCP tools aren't available at all, tell the user: "Run `/postman:setup` to configure the Postman MCP Server." -- If `searchLearningCenter` specifically is missing, call `getEnabledTools` to confirm the active tool set, then tell the user: "Searching the Learning Center requires Full mode. Unset `POSTMAN_MCP_MODE` (or set it to `mcp`) and restart Claude Code." +- If `searchLearningCenter` is missing, call `getEnabledTools` to confirm the active tool set, then split on which endpoint the route is pinned to. On a `/minimal` route it is absent by design and the user cannot change it from the client: say the Learning Center tool isn't part of that route's tool set and point them at https://learning.postman.com to search directly. On a `/mcp` route its absence is not a mode problem — the server isn't connected as configured: "Run `/postman:setup` to configure the Postman MCP Server." + +Do not answer a "how do I..." question from memory when the tool is unavailable. Cite only URLs the tool returned, or send the user to the Learning Center. ## Workflow @@ -64,6 +68,6 @@ To create a mock server in Postman: ## Error Handling - **MCP not configured:** "Run `/postman:setup` to configure the Postman MCP Server." -- **Tool unavailable (wrong mode):** Confirm with `getEnabledTools`, then: "Searching the Learning Center requires Full mode. Unset `POSTMAN_MCP_MODE` (or set it to `mcp`) and restart Claude Code." +- **`searchLearningCenter` unavailable:** Confirm with `getEnabledTools`. Expected on any route pinned to the `minimal` endpoint — say the tool isn't in that route's tool set and point the user at https://learning.postman.com. On a `/mcp` route: "Run `/postman:setup` to configure the Postman MCP Server." - **401 Unauthorized:** "Your Postman API key was rejected. Generate a new one at https://go.postman.co/settings/me/api-keys and run `/postman:setup`." - **No results:** "Nothing matched in the Learning Center. Try rephrasing with the Postman feature name, or ask about a more specific step."