@reqport/cli
v0.7.0
Published
Reqport responder CLI + MCP server — discover, read, and answer content-blind data requests (e.g. Engagemangskontroll / business-relationship checks) with an rqk_live_ API key.
Maintainers
Readme
@reqport/cli — qp
The official command-line client + MCP server for Reqport —
a publishable npx CLI (binary: qp) for Reqport responders — the data-holder side. Discover the requests addressed to
your organisation, read them, and answer them (e.g. Engagemangskontroll /
business-relationship checks as ISO 20022 auth.002). Humans can pair the CLI
with the Reqport console (qp login) to receive an API key without copy-pasting
from a web page.
# Automation (agents / CI): use an API key
export REQPORT_API_KEY="rqk_live_..." # PowerShell: $env:REQPORT_API_KEY="..."
npx @reqport/cli --env sandbox doctor # the bin is `qp`
# Humans: pair with the console — a browser opens, you approve, the key returns here
qp login --env sandbox
qp --env sandbox doctorThe package is
@reqport/cli; the single binary it installs isqp, sonpx @reqport/cli …andqp …are equivalent.
Why it's thin (the content-blind model)
Reqport stores only ciphertext, but the responder loop is server-assisted, so this CLI is just HTTP + an API key — no client-side crypto:
| Step | Endpoint | Who decrypts / seals |
|------|----------|----------------------|
| Read a request | POST /v1/payloads/decrypt-batch | The TEE decrypts in-enclave and returns plaintext to your authorized key |
| Answer (yes/no + accounts) | POST /v1/requests/{id}/business-relationship-response | The TEE seals the answer server-side |
No Model-1 dual-JWE, device keys, or DPoP are needed for this loop. (Uploading a
sealed answer document for the generic structured /response path is the only
flow that would require client-side encryption; it is intentionally not
implemented here.)
Credentials
The CLI authenticates with an rqk_live_ API key on every call. Two ways to
get one onto a machine:
- Set
REQPORT_API_KEY— for automation (agents / CI). Read at runtime only; never a flag, never logged. Needs scopespayloads:read+responses:writefor the responder loop. qp login(portal pairing) — for humans. A browser opens the Reqport console's CLI-auth page (which reuses your existing console session), you match a short confirmation code and approve, and the console mints a key and relays it back to the terminal, where it's stored as the active credential. No new OAuth client, no device flow, no localhost callback, no password ever seen by the CLI.
Precedence for requests / respond: REQPORT_API_KEY first, else the stored
paired key. Agents/CI should always use REQPORT_API_KEY.
The stored key lives at %APPDATA%\qp\credential.json (Windows) or
$XDG_CONFIG_HOME/qp/credential.json → ~/.config/qp/credential.json
(macOS/Linux), written 0600 (best-effort on Windows). qp logout clears it;
qp whoami shows its env / key id / scopes (local only).
Environments
--env sandbox|uat|prod (default sandbox, or REQPORT_ENV):
sandbox→https://sandbox.reqport.com/vantauat→https://vanta.dev-uat.reqport.com/vantaprod→https://vanta.reqport.com/vanta
Commands
| Command | What it does |
|---------|--------------|
| qp login [--env] [--portal-url <url>] [--name <keyName>] | Pair with the console; receive + store an API key |
| qp logout / qp whoami | Clear / show the stored key (local only) |
| qp doctor | Verify base URL, TEE attestation, and that your credential authenticates |
| qp requests list [--state open] [--type <t>] [--mine] | Discover requests via /v1/affordances |
| qp requests show <id> | Read one request; decrypts its content in the TEE |
| qp respond <id> … | Answer — auto-detects business-relationship vs generic |
| qp pending list | List this org's held responses awaiting approval |
| qp pending approve\|reject\|withdraw <id> | Release / reject / withdraw a held response |
| qp approval-policy get\|set <types> | Show / set which response types require approval before send |
| qp fir list [--state open] [--mine] | Discover FIR (Fraud Incident Response) cases addressed to you |
| qp fir show <firId> | Read a FIR case (content-blind metadata) |
| qp fir notify --file <notice.json> | Open a FIR case with a NOTICE (sending-bank side) |
| qp fir respond <firId> --outcome … | Answer a NOTICE with per-transaction outcomes (receiver side) |
| qp fir instruct-refund <firId> … | Instruct a refund of a held transaction (bank side) |
| qp fir confirm-refund <firId> --payload-id <uuid> | Confirm a refund executed (receiver side) |
| qp fir identity-request <firId> --transaction-ref … --about-party … --legal-basis … | Request the identity behind a fraud counterparty (either side) |
| qp fir identity-respond <firId> --record-status FOUND\|NOT_FOUND … | Return that identity (receiver side) — inline subject or a pre-sealed payload |
| qp fir update <firId> --update-type … | Post a lifecycle UPDATE (correction / law-enforcement ref / added transactions / status change / question / answer; either party) |
| qp fir close <firId> --reason … | Close a FIR case with a terminal reason (either party) |
| qp kyc list [--state open] [--mine] | Discover KYC / CDD checks addressed to you |
| qp kyc show <requestId> | Content-blind read-back of a KYC/CDD response |
| qp kyc respond <requestId> --record-status FOUND\|NOT_FOUND … | Answer a KYC/CDD request with the CDD record |
| qp keys status | Show the active credential's metadata (local only) |
| qp keys create\|list\|revoke | Points you to the console — key management is not a CLI operation |
| qp mcp | Run the stdio MCP server |
Global flags: --env, --json (machine-readable output on every command).
qp login (portal pairing)
qp login --env sandbox # default portal https://reqport.com
qp login --env sandbox --portal-url https://sandbox.reqport.com --name my-laptopThe CLI prints a confirmation code, opens
{portalUrl}/{locale}/developer/cli-auth?state=…&challenge=…&env=…, and polls
{portalUrl}/api/developer/cli-auth/poll until the console relays the key (or you
Ctrl+C). Override the portal with --portal-url / QP_PORTAL_URL (e.g. for
sandbox or aurora); locale via QP_LOCALE (default en).
Key management is in the console
Minting, listing, and revoking keys is done by a signed-in human in the developer
console ({portalUrl}/{locale}/developer). The CLI holds only an API key, and
Vanta's key-management endpoints are HUMAN-only (Signicat + ORG_ADMIN) — an API
key cannot call them — so qp keys create|list|revoke simply point you there.
Answering
# Business relationship check
qp respond <id> --has-relationship false --yes # auth.002 NFOU
qp respond <id> --has-relationship true \
--account ACCOUNT:SE1234567890123:IBAN:Main \
--account CARD:411111******1111:PAN --yes # auth.002 COMP
# ...optionally tag the relationship (recommended — enables a targeted follow-up)
qp respond <id> --has-relationship true \
--account ACCOUNT:SE1234567890123:IBAN:Main \
--relationship-types CUSTOMER,ACCOUNT_HOLDER --yes
# Generic request
qp respond <id> --status NFOU --yes
qp respond <id> --status COMP --free-text "See attached statement." --yes--account = TYPE:identifier[:scheme[:label]], TYPE ∈ ACCOUNT|WALLET|CARD,
repeatable. A true/COMP answer must disclose at least one account (or a
pre-sealed --payload-id).
--relationship-types = comma-separated tags attached to a true
business-relationship answer (ignored/omitted on a false answer). Optional but
recommended: it lets the requesting authority scope a targeted follow-up (data
minimisation). Valid values (exact): CUSTOMER, ACCOUNT_HOLDER,
BENEFICIAL_OWNER, AUTHORISED_REPRESENTATIVE, COUNTERPARTY,
FORMER_CUSTOMER, OTHER.
Approval (human-in-the-loop)
An org can require that some (or all) response types are approved by a human before they are sealed + sent. Submitted answers that match the policy are held until an approver releases them.
# See / set which response types require approval (sentinel ALL = every type)
qp approval-policy get
qp approval-policy set BUSINESS_RELATIONSHIP_RESPONSE
qp approval-policy set ALL
# Work the hold queue
qp pending list # id, request id, response type, auth.002 status, submitter, created
qp pending approve <id> # release — seal + send happens server-side
qp pending reject <id> --reason "…"
qp pending withdraw <id> # submitter withdraws their own held responseapprove / reject / withdraw are irreversible and prompt for confirmation on
an interactive terminal; pass -y/--yes (or --json) to skip the prompt.
Endpoints (all under the env's Vanta base, auth = your rqk_live_ key):
GET /v1/responses/pending (responses:read),
POST /v1/responses/pending/{id}/{approve|reject|withdraw} (responses:write),
GET/PUT /v1/responses/approval-policy (responses:read/responses:write).
FIR — Fraud Incident Response
A FIR is a multi-message FI-to-FI fraud case between a sending bank and
a receiving institution (a client-funds holder such as an exchange). Unlike the
single request→response families, a FIR case is one workflow instance (its id is the
firId) that carries the P0 messages plus an identity-exchange step over time:
| Step | Command | Role | Endpoint |
|------|---------|------|----------|
| NOTICE | qp fir notify | sending bank | POST /v1/fir/cases |
| RESPONSE | qp fir respond <firId> | receiver | POST /v1/fir/cases/{firId}/response |
| REFUND_INSTRUCTION | qp fir instruct-refund <firId> | sending bank | POST /v1/fir/cases/{firId}/refund-instruction |
| REFUND_CONFIRMATION | qp fir confirm-refund <firId> | receiver | POST /v1/fir/cases/{firId}/refund-confirmation |
| IDENTITY_REQUEST | qp fir identity-request <firId> | either party | POST /v1/fir/cases/{firId}/identity-request |
| IDENTITY_RESPONSE | qp fir identity-respond <firId> | counterparty receiver | POST /v1/fir/cases/{firId}/identity-response |
| UPDATE | qp fir update <firId> | either party | POST /v1/fir/cases/{firId}/update |
| CLOSE | qp fir close <firId> | either party | POST /v1/fir/cases/{firId}/close |
| read a case | qp fir show <firId> | either party | GET /v1/fir/cases/{firId} |
Roles. The bank opens the case (notify) and later authorises refunds
(instruct-refund). The receiver answers per-transaction (respond, one of
HELD | PROCESSED | PARTIAL | NEED_INFO) and confirms a refund executed
(confirm-refund). Everything is sealed dual-copy server-side — the CLI stays a
thin HTTP client (no client crypto).
Discovery. There is no list-cases endpoint. A receiver discovers incoming
cases through the same /v1/affordances discovery qp requests list uses — FIR
cases are FIR_FRAUD_CASE_V1 workflow instances addressed to the receiver.
qp fir list filters that discovery down to FIR cases and labels them.
# Receiver: find + read incoming cases
qp fir list
qp fir show <firId>Bodies: institutions + a payload
Every write body carries the two institutions (sender, recipient, each a
self-declared identity object) plus a payload. Institutions come from a
--file <path> / --body <inline JSON> base body, or inline via
--sender/--recipient; the payload can be built with convenience flags. Wire
field names are camelCase (transactionRef, accountType, heldAmount,
returnTo, recipientOrgId, …) and money.amount is always a decimal STRING.
# BANK: open a case (the nested NOTICE is easiest as a file)
qp fir notify --file ./notice.json
# notice.json = {"sender":{…},"recipient":{…},"recipientOrgId":"ORG_RECV",
# "notice":{"status":"SUSPECTED","requestedAction":"HOLD_FUNDS",
# "transactions":[{"transactionRef":"t1","rail":"INSTANT_CREDIT_TRANSFER",
# "amount":{"amount":"9300","currency":"SEK"},"executedAt":"2026-08-27T09:00:00Z",
# "receiver":{"accountType":"OMNIBUS","label":"klientmedel"}}]}}
# RECEIVER: answer per transaction (outcomes via repeatable flags)
qp fir respond <firId> --file ./parties.json \
--outcome t1:HELD:9300:SEK \
--outcome t2:NEED_INFO --note "one held, one needs info"
# --outcome <transaction_ref>:<HELD|PROCESSED|PARTIAL|NEED_INFO>[:<heldAmount>:<currency>]
# parties.json = {"sender":{…receiver institution…},"recipient":{…bank…}}
# BANK: instruct a refund of a held transaction
qp fir instruct-refund <firId> --file ./parties.json \
--transaction-ref t1 \
--return-iban SE45… --return-label "victim IBAN" \
--reference-text "fraud refund t1"
# RECEIVER: confirm the refund executed (carry a pre-sealed RefundExecution payload)
qp fir confirm-refund <firId> --file ./parties.json --payload-id <uuid>--outcome validates the outcome enum client-side (listing valid values on
error) before the POST; a held amount, when given, needs both amount and
currency and is carried as a string. notify validates the required top-level
fields and constrains each transaction's receiver.accountType to
CLIENT_FUNDS | OMNIBUS | MERCHANT (a receiver account is pooled / non-personal).
Confirm-refund and the approval hold. A confirm-refund carries a
pre-sealed RefundExecution (docType FIR_REFUND_JSON) as a --payload-id —
sensitive refund detail is never inlined. That lets the receiver's per-type
approval policy hold it for human review: when gated
the call returns PENDING_APPROVAL (release it with qp pending approve <id>);
otherwise it releases immediately.
Identity-exchange (identity-request / identity-response)
Once funds are held, a party may need the identity behind a fraud-transaction counterparty — the person or entity on the other side of a reported payment. That is a two-message exchange on the same case:
# EITHER PARTY: ask who is behind a transaction's counterparty
qp fir identity-request <firId> --file ./parties.json \
--transaction-ref t1 --about-party ORDER_CUSTOMER \
--legal-basis-scheme "SE-POLICE" --legal-basis-reference "DNR-2026-123" \
--requested-attribute name --requested-attribute dateOfBirth
# --about-party is ORDER_CUSTOMER | ORIGINATOR (validated client-side)
# legal basis: a free-text --legal-basis, OR structured
# --legal-basis-token / --legal-basis-scheme / --legal-basis-reference —
# at least one field is REQUIRED (the vanta gate, mirrored client-side)
# COUNTERPARTY RECEIVER: return the identity inline (the KYC/IVMS101 subject core)
qp fir identity-respond <firId> --file ./identity-subject.json \
--record-status FOUND --transaction-ref t1
# identity-subject.json = {"naturalPerson":{"name":{"primary":"Andersson",
# "secondary":"Anna"},"dateOfBirth":"1985-04-02","nationality":"SE"}}
# (a legalPerson subject is the same shape as KYC's legal_person, camelCase)
# …or return a pre-sealed identity document instead of inlining PII
qp fir identity-respond <firId> --sender '{…}' --recipient '{…}' \
--record-status FOUND --transaction-ref t1 --payload-id <uuid>
# …or decline: no counterparty on record
qp fir identity-respond <firId> --sender '{…}' --recipient '{…}' \
--record-status NOT_FOUND --transaction-ref t1The identity subject is the same IVMS101 identity core as
KYC / CDD — naturalPerson OR
legalPerson — but camelCase here (matching the FIR wire idiom), not KYC's
snake_case. Supply it inline under identityResponse.subject (server-sealed
per-party) OR as a pre-sealed --payload-id — the pre-sealed form is required
when your org's approval policy gates identity
disclosure (no cleartext PII may be held); a gated call returns PENDING_APPROVAL.
--record-status (FOUND | NOT_FOUND) and --about-party are validated
client-side before the POST.
Lifecycle (update / close)
Either party can post a lifecycle message on an open case. Like every other FIR
write, the body carries the two institutions (sender/recipient, from
--file/--body or inline --sender/--recipient) plus a nested payload
under update / close; the payload fields are snake_case (update_type,
law_enforcement_reference, free_text, reason):
POST …/{firId}/update { sender, recipient, update: { update_type, law_enforcement_reference?, status?, transactions?, free_text? } }
POST …/{firId}/close { sender, recipient, close: { reason, free_text? } }# EITHER PARTY: correct or extend the case (six update_types); parties from --file
qp fir update <firId> --file ./parties.json --update-type STATUS_CHANGE --status CONFIRMED
qp fir update <firId> --file ./parties.json --update-type LAW_ENFORCEMENT_REFERENCE_ADDED \
--law-enforcement-scheme "SE-POLICE" --law-enforcement-reference "DNR-2026-123"
qp fir update <firId> --file ./parties.json --update-type ADDITIONAL_TRANSACTIONS \
--transactions '[{"transactionRef":"t3","amount":{"amount":"500","currency":"SEK"}}]'
qp fir update <firId> --sender '{…}' --recipient '{…}' \
--update-type QUESTION --free-text "Any hold on t2 yet?"
# --update-type is CORRECTION | LAW_ENFORCEMENT_REFERENCE_ADDED |
# ADDITIONAL_TRANSACTIONS | STATUS_CHANGE | QUESTION | ANSWER (validated)
# STATUS_CHANGE REQUIRES --status (SUSPECTED|STRONG_SUSPICION|CONFIRMED|CLEARED)
# EITHER PARTY: close the case with a terminal reason
qp fir close <firId> --file ./parties.json --reason REFUNDED --free-text "full amount returned"
# --reason is REFUNDED | NOT_RECOVERABLE | NO_MATCH | WITHDRAWN | OTHER (validated)--update-type / --status / --reason are validated client-side (listing the
valid values on error) before the POST, and update --update-type STATUS_CHANGE
fails fast when --status is omitted. Both bodies can also be supplied whole via
--file / --body ({sender, recipient, update|close:{…}}); flags override the
payload fields.
The mutating FIR commands (notify, respond, instruct-refund,
confirm-refund, identity-request, identity-respond, update, close)
prompt for confirmation on an interactive terminal; pass -y/--yes (or --json)
to skip. Scopes: notify / instruct-refund / identity-request / update /
close need workflows:write; respond / confirm-refund / identity-respond
ride the response path (responses:write); show / list need workflows:read
/ discovery.
KYC / CDD — Customer Due Diligence response
A KYC / CDD check is a single request→response family (like the
business-relationship check), not a multi-message case: a requester (an
authority, or a peer FI carrying a legal basis) asks a bank or exchange for the
Customer Due Diligence record it holds on a subject, and the responder
answers. Like qp respond, the CLI answers requests — it does not create them.
| Step | Command | Endpoint |
|------|---------|----------|
| discover checks addressed to you | qp kyc list | GET /v1/affordances (filtered) |
| read back one answer (content-blind) | qp kyc show <requestId> | GET /v1/requests/{id}/kyc-response |
| submit the CDD record | qp kyc respond <requestId> | POST /v1/requests/{id}/kyc-response |
Discovery. There is no list endpoint. KYC checks are KYC_CDD_CHECK_V1
workflow instances that surface through the same /v1/affordances discovery
qp requests list uses; qp kyc list filters that down to KYC checks and labels
them.
⚠ Bodies are
snake_case. UNLIKE the rest of this CLI (and unlike FIR, which is camelCase), the KYC request body is snake_case —record_status,payload_id, and every nestedCddRecordfield (natural_person,kyc_status,risk_rating,national_identifier,beneficial_owners,source_of_funds, …). The record you pass with--file/--bodymust be snake_case JSON.
# Responder: discover + read
qp kyc list
qp kyc show <requestId>
# NOT_FOUND — a definitive negative (auth.002 NFOU); no record needed
qp kyc respond <requestId> --record-status NOT_FOUND --yes
# FOUND with an inline CDD record (auth.002 COMP; sealed per-party server-side)
qp kyc respond <requestId> --record-status FOUND --file ./cdd.json --yes
# cdd.json (snake_case) = {
# "subject": { "natural_person": {
# "name": { "primary": "Andersson", "secondary": "Anna" },
# "date_of_birth": "1985-04-12", "nationality": "SE",
# "national_identifier": { "scheme": "SE_PERSONNUMMER", "value": "…" } } },
# "verification": { "method": "BANK_ID", "level": "ENHANCED" },
# "kyc_status": "VERIFIED", "risk_rating": "LOW", "pep_status": "NONE",
# "relationship": { "status": "ACTIVE", "onboarded_at": "2021-02-01T00:00:00Z" },
# "queried_at": "2026-08-28T09:00:00Z" }
# FOUND via a pre-sealed, content-blind KYC_CDD_JSON document
qp kyc respond <requestId> --record-status FOUND --payload-id <uuid> --yes--record-status is mandatory and validated client-side against FOUND |
NOT_FOUND (FOUND → auth.002 COMP; NOT_FOUND → NFOU). The record is the
identity core (subject, a natural or legal person) plus an assessment
layer (verification, kyc_status, risk_rating + risk_factors,
pep_status + pep_position, screening, beneficial_owners,
source_of_funds, source_of_wealth, relationship, queried_at) — every field
optional; disclose only what is sufficient (data minimisation). When a record is
given, its enum fields (kyc_status, risk_rating, pep_status,
relationship.status) are spot-validated client-side before the POST.
Inline vs pre-sealed. Supply the record inline with --file <cdd.json> /
--body <inline JSON> (Vanta seals the assembled response per-party in the
RESPONSE_HEADER), or as a pre-sealed --payload-id (a validated,
content-blind KYC_CDD_JSON document Vanta never sees) — not both. A CDD record
carries PII: if your org's approval policy
gates KYC, an inline record is rejected (no cleartext PII may be held) and you
must resubmit as a pre-sealed --payload-id; the gated submit returns
PENDING_APPROVAL (release with qp pending approve <id>).
qp kyc respond prompts for confirmation on an interactive terminal; pass
-y/--yes (or --json) to skip. Scopes: respond needs responses:write;
show needs responses:read; list uses discovery.
The qp login ↔ portal pairing contract
The portal implements the server half; the CLI implements this half. For reference:
- CLI generates a secret
verifier+ randomstate, andchallenge = base64url(SHA256(verifier)). It prints a confirmation code (the first 8 chars ofstate) and opensGET {portalUrl}/{locale}/developer/cli-auth?state={state}&challenge={challenge}&env={env}[&name={name}]. - The human (already signed in to the console) verifies the code matches and
approves. The portal mints an
rqk_live_key bound tostate. - CLI polls
POST {portalUrl}/api/developer/cli-auth/pollwith{state, verifier}every ~2.5s (5-min timeout, clean Ctrl+C):202 {status:"pending"}→ keep polling200 {status:"ready", apiKey, keyId, env, scopes, expiresAt}→ store & stop403(verifier mismatch) /404(no session) /410(expired) → abort with a clear message
- CLI stores
{value: apiKey, kind:"apikey", env, keyId, scopes, expiresAt}as the active credential.
The portal authenticates the caller by requiring proof of verifier (whose
SHA-256 it received as challenge), so the minted key is only handed to the
client that started the pairing.
MCP server
npx @reqport/cli mcpA stdio MCP server (official @modelcontextprotocol/sdk) exposing the same
responder operations, built on the same client. API-key-only by design — an
agent cannot complete an interactive console pairing, so qp login is human/CLI
only. Configure the key in the server env:
{
"mcpServers": {
"reqport": {
"command": "npx",
"args": ["-y", "@reqport/cli", "mcp"],
"env": { "REQPORT_API_KEY": "rqk_live_...", "REQPORT_ENV": "sandbox" }
}
}
}Tools: reqport_doctor, reqport_list_requests, reqport_show_request,
reqport_decrypt_payloads, reqport_respond_business_relationship (accepts
relationshipTypes), reqport_respond, reqport_pending_list,
reqport_pending_approve, reqport_pending_reject, reqport_pending_withdraw,
reqport_approval_policy_get, reqport_approval_policy_set, the FIR tools
reqport_fir_list, reqport_fir_show, reqport_fir_notify, reqport_fir_respond,
reqport_fir_instruct_refund, reqport_fir_confirm_refund,
reqport_fir_identity_request, reqport_fir_identity_respond, the KYC / CDD tools
reqport_kyc_list, reqport_kyc_show, reqport_kyc_respond (bodies snake_case),
and the chat + attachment tools.
Give this to your agent
Paste this into a coding agent (Claude Code, Cursor, …) to build a responder integration end-to-end against the sandbox:
You are integrating our company as a Reqport responder. Use the
qpCLI (npx @reqport/cliinstalls theqpbinary). Reqport is content-blind but the responder loop is server-assisted, so you do not write any encryption code — reads decrypt in the TEE and answers are sealed server-side.
- I will export
REQPORT_API_KEY(anrqk_live_sandbox key). Never print, hardcode, or commit it. Always use--env sandbox; never call prod. (If I have not given you a key, ask me to runqp login --env sandbox— a browser pairing with the console — or to make one on the console/developerpage, and I'll export it for you.)- Run
qp --env sandbox doctorand confirm attestation + auth pass.- Run
qp --env sandbox requests listto find open requests addressed to us. Sandbox is seeded by a synthetic authority (reqport-authority-test.com); if the list is empty, tell me — do not invent data.- For a business-relationship check:
qp requests show <id>to read it, thenqp respond <id> --has-relationship false --yes(no customer → auth.002 NFOU) orqp respond <id> --has-relationship true --account ACCOUNT:<iban>:IBAN:<label> --yes(→ auth.002 COMP, accounts sealed in the TEE).- Verify with
qp requests show <id> --jsonthat status isRESPONDEDwith the expected outcome. Use--jsonthroughout for assertable output.If you prefer tool calls over shell, run
npx @reqport/cli mcp(a stdio MCP server with the same operations) configured withREQPORT_API_KEY+REQPORT_ENV=sandboxin its env.
An agent skill is bundled at .claude/skills/reqport-responder/SKILL.md.
Development
npm install # also builds via prepare
npm run build # tsc → dist/
npm run dev -- --env sandbox doctor # run from source with tsxNode >= 18. Uses the built-in global fetch; no network dependency beyond it.
Exact-pinned deps: commander, @modelcontextprotocol/sdk, zod.
Releasing
Releases publish to npm via OIDC Trusted Publishing with provenance — no
long-lived token in CI. Bump version, push a matching v* tag, and
.github/workflows/release.yml builds and runs npm publish --provenance under
the workflow's OIDC identity. This requires a one-time Trusted Publisher entry
(Reqport/reqport-cli + release.yml) configured on the npm package's settings
page. Until a release is published, run locally with node dist/index.js … or
npm link.
Links · npm · Developer docs · Source · Report an issue
