---
title: "Request a link for a human to enter a provider secret"
method: POST
path: "/api/v1/credentials/{provider}/request"
tags: ["Credentials"]
---

# Request a link for a human to enter a provider secret

`POST /api/v1/credentials/{provider}/request`

## Request

- Base URL: `https://wardenpoint.com` (the document declares no server; this is the origin it was published from)
- URL: `https://wardenpoint.com/api/v1/credentials/{provider}/request`
- Auth: API key in header `X-API-Key`

There is deliberately no endpoint that accepts a secret. A secret named to an agent ends up in the model context, in transport logs and in the conversation history, and none of those can be wiped afterwards. So instead you ask for a link, show it to a person, and poll for the outcome.

The link opens a one-field form inside the dashboard: the person pastes the value straight from their clipboard, the form checks it against the provider and stores it. You never see the value, and neither does this API.

The link is short-lived (15 minutes by default), bound to this one provider, and only usable by someone signed in to the same account — an unauthenticated visitor gets the normal login page first. It is spent by one SUCCESSFUL entry: a rejected value leaves the link usable so a typo does not send the person back to you for a new one.

Not idempotent, on purpose: repeating the call means "the previous link was lost or expired", so it issues a fresh one. Old links stay valid until they expire.

This does NOT replace the dashboard: a person still has to be at a screen. What it removes is the secret travelling through you.

## Path parameters

- `provider` 'telegram_bot' | 'telegram' | 'custom_asterisk' | 'whatsapp' | 'viber' | 'telegram_2fa', required

## Request body

- object
  - `provider` string: Accepted and then overwritten by the path — declared here only so that a schema-validating client is not stricter than the server. Sending it changes nothing.

## Response `201`

Link issued.

- object
  - `data` CredentialRequest: One request for a human to type a provider secret. Never carries the secret itself, in any direction.
    - `uuid` string, uuid: Stable identifier of the request.
    - `provider` string: The provider this request is bound to. A request is good for one provider only.
    - `status` 'pending' | 'verified' | 'failed': pending — nobody has typed anything yet. verified — the secret was accepted by the provider and stored; the link is spent. failed — see failure_reason.
    - `failure_reason` 'verification_failed' | 'expired' | 'superseded', nullable: Only set when status is failed. `verification_failed` — the provider rejected what was typed; this is NOT terminal, the same link keeps working until expires_at and the status can still become verified. `expired` — terminal, ask for a new link. `superseded` — terminal and NOT an error: the secret for this provider was already entered through another link of the same company (asking for a link twice, which a retry does, produces exactly this), so the one you are polling was closed and the credential you wanted is in place. Check the other request, or the channel itself, before asking for a new link.
    - `verification` 'provider_check' | 'stored_only': What `verified` means for this provider. `provider_check` — the value was accepted by the provider itself, so the channel works. `stored_only` — the values were stored but nothing proved them here, because this provider cannot be checked by typing alone; today that is only `telegram`, where the proof is the QR sign-in (POST /api/v1/credentials/telegram/qr). Do not report an account as configured on a `stored_only` verified.
    - `url` string, uri: The page to show the human. Opening it requires being signed in to this account, so a leaked link is useless on its own.
    - `status_url` string, uri: Poll this for the status. Polling has no side effects and never returns the secret.
    - `expires_at` string, date-time: After this moment the link stops working and the status reads failed/expired.
    - `created_at` string, date-time
    - `completed_at` string, date-time, nullable: When the secret was accepted. Null until then.

## Other responses

- `401`: API key missing, invalid, revoked or expired.
- `403`: API key lacks the config.write ability. There is no secrets.write ability — asking for the link is a config.write action, and typing the secret is not an API action at all.
- `404`: Secret-entry links are switched off for this installation.
- `422`: This provider does not accept a secret-entry link; the message lists the ones that do.

---

[API](https://skmtc.dev/wardenpoint/apis/wardenpoint-api.md) · [All operations](https://skmtc.dev/wardenpoint/apis/wardenpoint-api/llms.txt) · [OpenAPI document](https://skmtc.dev/wardenpoint/apis/wardenpoint-api/revisions/259ee445728e?raw)
