POST Verify Identity / Re-Login
Re-verify / refresh the saved login for an existing identity — works for 1Password-sourced and manually-entered credentials
https://api.anakin.io/v1/wire/credentials/verifyRe-verify and refresh the saved login for an existing identity. Wire re-runs the catalog's login wheel and refreshes the stored session cookies in place. Works for all identity types:
- 1Password-sourced credential — omit
params(or send{}). The engine re-resolves the username and password from the bound vault item. - Manually-entered credential (Plaid-style, no bound source) — send the catalog's login
params. There is nothing to re-resolve, so the fields are required.
This is the same endpoint the dashboard Re-verify action uses, for both kinds of credential.
The password is never stored. Only the issued session cookies are persisted, encrypted. When the cookies expire (see
expires_at), call this endpoint again to mint fresh ones.
It behaves like POST /v1/wire/login, but targets a known identity_id instead of finding-or-creating an identity.
Request Body
{
"identity_id": "7c3f1a2b-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"params": {
"username": "alice@example.com",
"password": "..."
}
}| Parameter | Type | Description |
|---|---|---|
identity_id required | string (UUID) | The identity whose credential should be re-verified. |
params | object | The catalog's login fields (same shape as POST /login). Omit or send {} for a 1Password-sourced credential — the engine re-resolves the fields from the bound source. Required when the credential has no bound source (manual credentials must send params). |
Response
201 Created{
"status": "verified",
"credential": {
"id": "11111111-2222-3333-4444-555555555555",
"identity_id": "7c3f1a2b-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"credential_type": "credentials",
"status": "active",
"source_id": "f1e2d3c4-0000-0000-0000-000000000000",
"source_ref": {
"vault_id": "abcd1234",
"item_id": "wxyz5678",
"fields": ["username", "password"]
},
"expires_at": "2026-06-09T10:00:00Z",
"metadata": {},
"created_at": "2026-06-08T10:00:00Z",
"updated_at": "2026-06-08T10:00:00Z"
}
}| Field | Type | Description |
|---|---|---|
status | string | Always "verified" on success |
credential.id | string (UUID) | Pass this as credential_id in subsequent POST /v1/wire/task calls. |
credential.identity_id | string (UUID) | The identity this credential belongs to |
credential.credential_type | string | Credential kind, e.g. credentials |
credential.status | string | Credential status — active after a successful re-verify |
credential.source_id | string (UUID) | The bound identity source. Present only for sourced credentials (omitted for manual). |
credential.source_ref | object | The bound vault item locator (vault_id, item_id, fields). Present only for sourced credentials. |
credential.expires_at | string | RFC3339 UTC. When the refreshed session expires. |
credential.metadata | object | Free-form metadata for the credential (always present). |
credential.created_at | string | RFC3339 UTC. When the credential was created. |
credential.updated_at | string | RFC3339 UTC. When the credential was last updated (always present). |
source_idandsource_refappear only for 1Password-sourced credentials; they are omitted for manually-entered ones.
Error Responses
All errors return JSON of the form { "status": "error", "error": { "code": "...", "message": "..." } }. The wheel's raw error text is never echoed; only the typed code is surfaced.
Validation / setup — request rejected before the wheel runs:
| Code | HTTP | When |
|---|---|---|
INVALID_BODY | 400 | Request body isn't valid JSON |
INVALID_INPUT | 400 | identity_id missing; OR params empty and no integration is bound to this credential (manual credentials must send params) |
NOT_FOUND | 404 | Identity not found |
FORBIDDEN | 403 | Identity belongs to another user |
LOGIN_NOT_SUPPORTED | 400 | The catalog doesn't support credentials login, or has no published login action |
LOGIN_NOT_AVAILABLE | 503 | Credentials-mode login isn't configured on this engine |
LOGIN_TRANSPORT_ERROR | 502 | Could not reach the wheel runner |
Source resolution — only when re-resolving a bound 1Password credential, before the wheel runs:
| Code | HTTP | When |
|---|---|---|
SOURCE_NOT_FOUND | 400 | The credential's bound integration was deleted — reconnect the source |
SOURCE_REVOKED | 400 | 1Password rejected the stored token — reconnect the source in the dashboard |
SOURCE_ITEM_NOT_FOUND | 400 | The bound vault/item no longer exists |
SOURCE_ITEM_MISSING_FIELDS | 400 | The item has no username or password |
SOURCE_INACTIVE | 400 | The bound source is not active |
SOURCE_NOT_AVAILABLE | 503 | Source resolver not configured on this engine |
Wheel ran (HTTP 200, status: "error") — the request was well-formed and the wheel ran, but sign-in didn't complete. Treat any status != "verified" as failure.
| Code | HTTP | When |
|---|---|---|
BAD_PASSWORD | 200 | Sign-in rejected by the site — wrong credentials |
MFA_REQUIRED | 200 | Account requires multi-factor auth (not yet supported) |
CAPTCHA_REQUIRED | 200 | Site is showing a captcha challenge |
ACCOUNT_LOCKED | 200 | Site reports the account is locked |
LOGIN_TIMEOUT | 200 | Sign-in took too long |
LOGIN_PAGE_CHANGED | 200 | Site's login flow may have changed |
LOGIN_NO_COOKIES | 200 | Sign-in completed but no session cookie was issued |
LOGIN_INFRASTRUCTURE_ERROR | 200 | Transient Browser Connect / upstream issue — retry (same as /login) |
LOGIN_FAILED | 200 | Generic fallback — sign-in failed for an unclassified reason |
HTTP 200 with
status: "error"is used for "the request was well-formed and the wheel ran, but the sign-in itself didn't complete." Treat anystatus != "verified"as failure regardless of HTTP code.
Code Examples
# (a) 1Password-sourced credential — no params; fields re-resolved from the bound item
curl https://api.anakin.io/v1/wire/credentials/verify \
-X POST \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{ "identity_id": "7c3f1a2b-4d5e-6f7a-8b9c-0d1e2f3a4b5c" }'
# (b) manual credential — send the catalog's login params
curl https://api.anakin.io/v1/wire/credentials/verify \
-X POST \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"identity_id": "7c3f1a2b-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"params": {
"username": "alice@example.com",
"password": "your_password"
}
}'import requests
response = requests.post(
'https://api.anakin.io/v1/wire/credentials/verify',
headers={'X-API-Key': 'your_api_key'},
json={
'identity_id': '7c3f1a2b-4d5e-6f7a-8b9c-0d1e2f3a4b5c',
'params': {
'username': 'alice@example.com',
'password': 'your_password',
},
},
)
data = response.json()
if data['status'] == 'verified':
credential_id = data['credential']['id']
# Use credential_id with /v1/wire/task
else:
print(f"Verify failed: {data['error']['code']} — {data['error']['message']}")const response = await fetch('https://api.anakin.io/v1/wire/credentials/verify', {
method: 'POST',
headers: {
'X-API-Key': 'your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
identity_id: '7c3f1a2b-4d5e-6f7a-8b9c-0d1e2f3a4b5c',
params: {
username: 'alice@example.com',
password: 'your_password',
},
}),
});
const data = await response.json();
if (data.status === 'verified') {
const credentialId = data.credential.id;
// Use credentialId with /v1/wire/task
} else {
console.error(`Verify failed: ${data.error.code} — ${data.error.message}`);
}Rate limit
10 requests per minute per user (each call runs a real sign-in against the target site).
Related
- GET /v1/wire/identities — list identities + credentials
- POST /v1/wire/login — single-call find-or-create credentials login
- POST /v1/wire/login — Create Identity / Login (password or 1Password)
- POST /v1/wire/task — use the returned
credential.idto run an authenticated action - Identity Sources guide — concepts and walkthrough