POST Connect Source

Connect a vault you own — 1Password or Azure Key Vault — so logins can read their credentials from it

POSThttps://api.anakin.io/v1/wire/identity-sources

Connects a vault you own so logins can read credentials from it instead of you typing a password into Wire.

The body is generic: provider, display_name, an optional auth_method, and then whatever fields that provider declares — at the top level, not nested. Read those fields from GET /v1/wire/identity-sources/providers rather than hardcoding them.

A failed connect stores nothing. Wire proves the credential against the provider before persisting — for Azure, against every vault URL you supplied. There is no half-built connection to clean up, and a rejected credential never becomes a broken source.

Credentials are encrypted at rest (AES-256-GCM) and never returned by any endpoint.


Request Body

ParameterTypeDescription
provider requiredstringProvider key from list providers1password or azure_key_vault
display_name requiredstringYour label for the connection. Not required to be unique — renames after rotation are common
auth_methodstringWhich of the provider's methods[].id to use. Omit for the provider's default (methods[0])
provider fieldsstringEvery field the chosen method declares, top-level. Values must be strings — a non-string is rejected rather than coerced

1Password — one field, a Service Account token:

{
  "provider": "1password",
  "display_name": "Engineering vault",
  "token": "ops_..."
}

Create the token under Developer Tools → Service Accounts in 1Password, granting it read access to the vaults you want Wire to use — see Provider Setup.

Azure Key Vault — an Entra service principal plus the vaults it may read:

{
  "provider": "azure_key_vault",
  "display_name": "Prod Key Vault",
  "auth_method": "service_principal",
  "tenant_id": "00000000-0000-0000-0000-000000000000",
  "client_id": "00000000-0000-0000-0000-000000000000",
  "client_secret": "...",
  "vault_urls": "https://my-vault.vault.azure.net\nhttps://other-vault.vault.azure.net"
}

vault_urls is newline-separated. Azure has no API for "which vaults may this principal read", so you list them — and the app must hold the Key Vault Secrets User role on each. That role assignment is the step most often missed: without it the connection authenticates but reads nothing, and connect fails naming the vault. Full walkthrough: Provider Setup.


Response

201 Created
{
  "status": "ok",
  "identity_source": {
    "id": "f1e2d3c4-0000-0000-0000-000000000000",
    "user_id": "8a7b6c5d-0000-0000-0000-000000000000",
    "provider": "1password",
    "display_name": "Engineering vault",
    "config": { "auth_method": "service_account" },
    "scope_metadata": { "vaults": [ { "id": "abcd1234", "name": "Engineering" } ] },
    "status": "active",
    "created_at": "2026-06-01T09:00:00Z",
    "updated_at": "2026-06-01T09:00:00Z"
  }
}

Same shape as an element of GET /v1/wire/identity-sources. scope_metadata.vaults is discovered during the connect check, so you can show what the connection reaches without a second live call.

config only carries auth_method if you sent one. Omit auth_method — the common case, since it defaults to methods[0] — and config comes back {}. The connection still works identically; a later rotation simply falls back to the provider's default method rather than reusing a recorded one.

You can connect the same provider more than once — one source per vault, or dev and prod side by side. Display-name collisions aren't blocked either.


Error Responses

All errors return JSON of the form { "status": "error", "error": { "code": "...", "message": "..." } }. Provider-specific codes carry the provider's own guidance in message.

CodeHTTPWhen
INVALID_BODY400Body isn't a JSON object, or a field's value isn't a string
INVALID_INPUT400provider or display_name missing, or a required provider field is blank
UNSUPPORTED_PROVIDER400This engine has no source registered for that provider
UNSUPPORTED_AUTH_METHOD400The provider doesn't offer that auth_method
UNKNOWN_CONNECT_FIELD400A field name the chosen method doesn't declare — usually a typo
INVALID_TOKEN_FORMAT400(1Password) The token doesn't start with ops_
INVALID_VAULT_URL400(Azure) A vault_urls entry is malformed, isn't an Azure Key Vault address, or no Key Vault exists at it. Hosts must end in .vault.azure.net, .vault.usgovcloudapi.net or .vault.azure.cn — arbitrary URLs are refused, so Wire can't be pointed at a host of your choosing
SOURCE_TOKEN_REJECTED400The provider rejected the credentials. Check them and the access they grant
SOURCE_FORBIDDEN400Authentication succeeded but the credential can't read the requested vault. Grant the role, then retry
SOURCE_ITEM_NOT_FOUND400The referenced vault was not found
SOURCE_THROTTLED429The provider is rate-limiting us. Retry shortly
SOURCE_VERIFY_FAILED400Could not reach the provider to verify. Retry in a moment
SOURCE_NOT_AVAILABLE503The source resolver isn't configured on this engine

SOURCE_TOKEN_REJECTED and SOURCE_FORBIDDEN look alike and mean opposite things. The first says the credential is wrong; the second says the credential is fine but hasn't been granted the vault. Only the second is fixed at the provider without issuing a new secret.


Code Examples

# 1Password
curl https://api.anakin.io/v1/wire/identity-sources \
  -X POST \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "1password",
    "display_name": "Engineering vault",
    "token": "ops_your_service_account_token"
  }'

# Azure Key Vault
curl https://api.anakin.io/v1/wire/identity-sources \
  -X POST \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "azure_key_vault",
    "display_name": "Prod Key Vault",
    "tenant_id": "00000000-0000-0000-0000-000000000000",
    "client_id": "00000000-0000-0000-0000-000000000000",
    "client_secret": "your_client_secret",
    "vault_urls": "https://my-vault.vault.azure.net"
  }'

Rate limit

20 requests per minute per user (each call verifies live against the provider).