POST Connect Source
Connect a vault you own — 1Password or Azure Key Vault — so logins can read their credentials from it
https://api.anakin.io/v1/wire/identity-sourcesConnects 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
| Parameter | Type | Description |
|---|---|---|
provider required | string | Provider key from list providers — 1password or azure_key_vault |
display_name required | string | Your label for the connection. Not required to be unique — renames after rotation are common |
auth_method | string | Which of the provider's methods[].id to use. Omit for the provider's default (methods[0]) |
| provider fields | string | Every 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.
configonly carriesauth_methodif you sent one. Omitauth_method— the common case, since it defaults tomethods[0]— andconfigcomes 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.
| Code | HTTP | When |
|---|---|---|
INVALID_BODY | 400 | Body isn't a JSON object, or a field's value isn't a string |
INVALID_INPUT | 400 | provider or display_name missing, or a required provider field is blank |
UNSUPPORTED_PROVIDER | 400 | This engine has no source registered for that provider |
UNSUPPORTED_AUTH_METHOD | 400 | The provider doesn't offer that auth_method |
UNKNOWN_CONNECT_FIELD | 400 | A field name the chosen method doesn't declare — usually a typo |
INVALID_TOKEN_FORMAT | 400 | (1Password) The token doesn't start with ops_ |
INVALID_VAULT_URL | 400 | (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_REJECTED | 400 | The provider rejected the credentials. Check them and the access they grant |
SOURCE_FORBIDDEN | 400 | Authentication succeeded but the credential can't read the requested vault. Grant the role, then retry |
SOURCE_ITEM_NOT_FOUND | 400 | The referenced vault was not found |
SOURCE_THROTTLED | 429 | The provider is rate-limiting us. Retry shortly |
SOURCE_VERIFY_FAILED | 400 | Could not reach the provider to verify. Retry in a moment |
SOURCE_NOT_AVAILABLE | 503 | The source resolver isn't configured on this engine |
SOURCE_TOKEN_REJECTEDandSOURCE_FORBIDDENlook 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"
}'import requests
response = requests.post(
'https://api.anakin.io/v1/wire/identity-sources',
headers={'X-API-Key': 'your_api_key'},
json={
'provider': '1password',
'display_name': 'Engineering vault',
'token': 'ops_your_service_account_token',
},
)
data = response.json()
if data['status'] == 'ok':
print(f"Connected: {data['identity_source']['id']}")
else:
print(f"Error: {data['error']['code']} — {data['error']['message']}")const response = await fetch(`https://api.anakin.io/v1/wire/identity-sources`, {
method: 'POST',
headers: {
'X-API-Key': 'your_api_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
provider: '1password',
display_name: 'Engineering vault',
token: 'ops_your_service_account_token',
}),
});
const data = await response.json();
if (data.status === 'ok') {
console.log(`Connected: ${data.identity_source.id}`);
} else {
console.error(`Error: ${data.error.code} — ${data.error.message}`);
}Rate limit
20 requests per minute per user (each call verifies live against the provider).
Related
- GET /v1/wire/identity-sources/providers — the fields to send, per provider
- GET /v1/wire/identity-sources/{id}/containers — browse what the new source can read
- PATCH /v1/wire/identity-sources/{id} — rename or rotate the credential
- POST /v1/wire/login — sign in using an entry from this source