Credential Vault
In the v0.4.3 dashboard this vault lives under Connections → Credentials. The mechanics below are unchanged; only the navigation moved.
Most sources don't take a raw secret inline. Instead they reference a secret stored in the named credential vault by its id. You create the credential once, then point one or more sources at it. The source's poll worker resolves the id to the decrypted secret at poll time — the secret is never written into a sensor_configs row and never echoed back by the API.
The vault is org-scoped and admin-only: every endpoint requires an org admin. Credentials are shared between the sensors that ingest and the Workers that act — anything consuming the same secret shape reuses one credential (a GitHub PAT, for example, is one github_token credential whether a GitHub sensor reads with it or a GitHub-issue Worker writes with it).
Credential types
Each credential has a type that declares its shape. The types a source can reference:
| Type | Used by | Secret shape |
|---|---|---|
github_token | GitHub source | GitHub PAT with read access to the org/repos |
slack_bot_token | Slack sensor; slack_bot Worker delivery channel (UI label Slack Bot); @corveil chat | Bot token, xoxb-… (chat:write / reactions:write to deliver and ACK) |
slack_signing_secret | Slack Events API (/webhooks/slack/{token}) | Signing Secret from the Slack app Basic Information page — not the bot token |
google_service_account_json | Google Meet, Google Calendar, Google Drive, Gmail sources | Full service-account key JSON |
jira_api_token | Jira and Confluence Cloud sources | Atlassian Cloud API token |
linear_api_key | Linear source | Linear personal API key (lin_api_…) |
asana_pat | Asana source | Asana personal access token (opaque; often 1/{gid}:{secret}) |
monday_api_token | Monday.com source | Monday.com personal API token |
notion_token | Notion source | Notion internal integration token (ntn_… or legacy secret_…) |
airtable_pat | Airtable source | Airtable personal access token (pat…). Not a retired API key or an OAuth access token. |
zoom_s2s_oauth | Zoom Transcripts source | Zoom Server-to-Server OAuth JSON: {"account_id","client_id","client_secret"} |
atlassian_dc_pat | Jira Data Center and Confluence Data Center sources | Data Center personal access token (opaque; no ATATT prefix) |
shellcrm_api_key | shell-crm source | shell-crm API key (client_id:client_secret) |
microsoft_graph | Microsoft Teams, SharePoint / OneDrive, Outlook Calendar, and Outlook (email) sources | Entra ID app registration as JSON: {"tenant_id","client_id","client_secret"} |
gitlab_token | GitLab source | GitLab personal / group / project access token (glpat-…); read_api |
bitbucket_access_token | Bitbucket source | Bitbucket Cloud access token (preferred), app password, or Atlassian API token |
hubspot_access_token | HubSpot source | HubSpot private-app token or account Service Key (pat-…) |
intercom_access_token | Intercom source | Intercom private-app access token (Authorization: Bearer). Not an OAuth marketplace token. |
salesforce_connected_app | Salesforce source | Connected App JSON: {"login_url","client_id","client_secret"} |
greenhouse_harvest_api_key | Greenhouse source | Greenhouse Harvest API key (HTTP Basic username, empty password). Not a Job Board token. |
lever_api_key | Lever source | Lever API key (HTTP Basic username, empty password). Not an OAuth partner token. |
workday_api_client | Workday source | Workday API Client JSON: {"host","tenant","client_id","client_secret","refresh_token"} |
pagerduty_api_key | PagerDuty source | PagerDuty REST API key (Authorization: Token token=…). Not an OAuth access token. |
zendesk_api_token | Zendesk source | Zendesk API token (HTTP Basic username {email}/token). Agent email and subdomain are sensor config, not this secret. |
bamboohr_api_key | BambooHR source | BambooHR API key (HTTP Basic username, password x). Company subdomain is sensor config, not this secret. |
rippling_api_token | Rippling source | Rippling REST API token (Authorization: Bearer). Not an OAuth app-install token. |
gusto_api_client | Gusto source | Gusto App Integrations OAuth JSON: {"client_id","client_secret","refresh_token","redirect_uri","environment"?}. Refresh tokens are single-use; Corveil rotates the vault row after mint. |
gong_access_key | Gong source | JSON {"access_key","access_key_secret"} from Gong Company Settings → API |
aws_access_key | Amazon S3 source (optional) | JSON {"access_key_id","secret_access_key","session_token"?}. Omit the credential to use the AWS default chain. |
Other types (slack_webhook, generic_webhook) exist for Worker delivery channels and are not referenced by sensors. The Jenkins and Webhook sources use no vault credential — their webhook tokens are generated server-side at create time.
The live list for your instance:
curl -sS https://your-corveil-host/api/credentials/types \
-H "Authorization: Bearer <admin-session-or-key>"Returns each registered type with its human-readable display name.
Create a credential
POST /api/credentials
{
"type": "github_token",
"name": "acme-readonly-pat",
"secret": "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}curl -sS -X POST https://your-corveil-host/api/credentials \
-H "Authorization: Bearer <admin-session-or-key>" \
-H "Content-Type: application/json" \
-d '{"type":"github_token","name":"acme-readonly-pat","secret":"ghp_..."}'The response echoes the stored credential's metadata — including its id — but never the secret. The secret is validated at write time for the given type, so a malformed value is rejected with a 400 before it lands in the vault.
Copy the returned id; that's what you put in a source's credentials block.
List, read, update, delete
| Method & path | Purpose |
|---|---|
GET /api/credentials | List all credentials (metadata only). Add ?type=github_token to filter. |
GET /api/credentials/{id} | Fetch one credential's metadata. |
PATCH /api/credentials/{id} | Rename ({"name": "..."}) or rotate the secret ({"secret": "..."}). |
DELETE /api/credentials/{id} | Remove a credential. |
How a source references a credential
When you create a source, the credentials block holds the credential id (a UUID), not the secret:
{
"sensor_type": "github",
"name": "Acme GitHub",
"config": { "org": "acme" },
"credentials": { "token": "6f1c2b8e-0a4d-4d6a-9f2e-1c3b5a7d9e11" }
}The token key is the credential field the GitHub source declares; its value is the id returned by POST /api/credentials. Every source guide in this section lists exactly which credential type its credential field expects.
The Add sensor UI renders a dropdown of matching credentials for each credential field, so you rarely assemble this JSON by hand — but the same shape is what the API expects for programmatic setup.