Skip to main content

API & Webhooks

DotVault provides a comprehensive REST API and webhook system for programmatic access and integrations.

API Overview

Base URL

https://api.dotvault.io/v1

Authentication

API Key (Recommended for server-to-server):

curl -H "Authorization: Bearer dv_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
https://api.dotvault.io/v1/projects

OAuth 2.0 (For user-facing applications):

# 1. Obtain access token
POST /oauth/token
{
"grant_type": "authorization_code",
"client_id": "your-client-id",
"code": "auth-code",
"redirect_uri": "https://your-app.com/callback"
}

# 2. Use access token
curl -H "Authorization: Bearer access_token_xxx" \
https://api.dotvault.io/v1/projects

Rate Limiting

PlanRequests/MinuteBurst
Free6010
Pro600100
Enterprise60001000

Headers returned:

X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1640995200

Response Format

All responses follow this structure:

{
"data": { ... },
"meta": {
"page": 1,
"limit": 20,
"total": 150,
"hasMore": true
}
}

Errors:

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": [
{
"field": "name",
"message": "Name is required"
}
]
}
}

Core Endpoints

Projects

List Projects:

GET /v1/projects?page=1&limit=20

Response:
{
"data": {
"projects": [
{
"id": "proj_xxx",
"name": "My Project",
"slug": "my-project",
"role": "owner",
"memberCount": 5,
"envCount": 3,
"createdAt": "2024-01-15T10:30:00Z"
}
]
},
"meta": { ... }
}

Create Project:

POST /v1/projects
{
"name": "New Project",
"slug": "new-project"
}

Response:
{
"data": {
"id": "proj_xxx",
"name": "New Project",
"slug": "new-project",
"createdAt": "2024-01-15T10:30:00Z"
}
}

Get Project:

GET /v1/projects/{projectId}

Response:
{
"data": {
"id": "proj_xxx",
"name": "My Project",
"slug": "my-project",
"members": [...],
"envs": [...],
"settings": { ... }
}
}

Update Project:

PATCH /v1/projects/{projectId}
{
"name": "Updated Name",
"ipAllowlist": "192.168.1.0/24"
}

Delete Project:

DELETE /v1/projects/{projectId}

Environments

List Environments:

GET /v1/projects/{projectId}/envs

Response:
{
"data": {
"envs": [
{
"id": "env_xxx",
"label": "production",
"variableCount": 12,
"lastModifiedAt": "2024-01-15T10:30:00Z",
"lastModifiedBy": { ... }
}
]
}
}

Get Environment:

GET /v1/projects/{projectId}/envs/{envId}

Response:
{
"data": {
"id": "env_xxx",
"label": "production",
"content": "DATABASE_URL=postgres://...\nAPI_KEY=sk_...",
"variables": [
{
"key": "DATABASE_URL",
"value": "postgres://...",
"createdAt": "2024-01-15T10:30:00Z"
}
],
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
}

Create Environment:

POST /v1/projects/{projectId}/envs
{
"label": "staging",
"content": "DATABASE_URL=postgres://...\nAPI_KEY=sk_..."
}

Update Environment:

PATCH /v1/projects/{projectId}/envs/{envId}
{
"content": "DATABASE_URL=postgres://...\nAPI_KEY=sk_...\nNEW_VAR=value"
}

Delete Environment:

DELETE /v1/projects/{projectId}/envs/{envId}

Members

List Members:

GET /v1/projects/{projectId}/members

Response:
{
"data": {
"members": [
{
"id": "user_xxx",
"name": "John Doe",
"email": "john@example.com",
"role": "editor",
"joinedAt": "2024-01-15T10:30:00Z"
}
]
}
}

Invite Member:

POST /v1/projects/{projectId}/invitations
{
"email": "new@example.com",
"role": "viewer"
}

Update Member Role:

PATCH /v1/projects/{projectId}/members/{userId}
{
"role": "editor"
}

Remove Member:

DELETE /v1/projects/{projectId}/members/{userId}

Audit Logs

List Audit Logs:

GET /v1/projects/{projectId}/audit-logs?action=env_update&from=2024-01-01

Response:
{
"data": {
"logs": [
{
"id": "log_xxx",
"action": "env_update",
"user": { ... },
"resourceType": "env",
"resourceId": "env_xxx",
"metadata": { ... },
"ipAddress": "192.168.1.1",
"createdAt": "2024-01-15T10:30:00Z"
}
]
},
"meta": { ... }
}

Webhooks

Overview

Webhooks enable real-time notifications when events occur in your DotVault projects.

Event Types

EventDescription
env.createdEnvironment created
env.updatedEnvironment modified
env.deletedEnvironment deleted
env.viewedEnvironment accessed
member.invitedMember invited
member.joinedMember accepted invitation
member.removedMember removed
access.requestedAccess request submitted
access.approvedAccess request approved
emergency.access.usedEmergency access activated
secret.rotatedSecret rotated
sync.completedEnvironment sync completed
login.failedFailed login attempt
login.succeededSuccessful login

Creating Webhooks

Via API:

POST /v1/projects/{projectId}/webhooks
{
"type": "slack",
"url": "https://hooks.slack.com/services/...",
"events": ["env.updated", "env.deleted"],
"secret": "webhook-signing-secret"
}

Response:
{
"data": {
"id": "whk_xxx",
"type": "slack",
"url": "https://hooks.slack.com/services/...",
"events": ["env.updated", "env.deleted"],
"isActive": true,
"createdAt": "2024-01-15T10:30:00Z"
}
}

Via CLI:

dotvault webhook add my-project \
--type slack \
--url https://hooks.slack.com/services/... \
--events env.updated,env.deleted

Webhook Payloads

Generic Webhook Format:

{
"event": "env.updated",
"projectId": "proj_xxx",
"projectName": "My Project",
"user": {
"id": "user_xxx",
"name": "John Doe",
"email": "john@example.com"
},
"timestamp": "2024-01-15T10:30:00.000Z",
"details": {
"label": "production",
"via": "api"
},
"ipAddress": "192.168.1.1",
"source": "dotvault",
"version": "1.0"
}

Slack Format:

{
"attachments": [
{
"color": "#3b82f6",
"title": "✏️ Environment Updated",
"fields": [
{
"title": "Project",
"value": "My Project",
"short": true
},
{
"title": "User",
"value": "John Doe (john@example.com)",
"short": true
}
],
"footer": "DotVault",
"ts": 1705315800
}
]
}

Signature Verification

Verify webhook authenticity using HMAC-SHA256:

Node.js:

const crypto = require("crypto");

function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(JSON.stringify(payload))
.digest("hex");

return signature === `sha256=${expected}`;
}

app.post("/webhook", (req, res) => {
const signature = req.headers["x-dotvault-signature"];
const secret = process.env.DOTVAULT_WEBHOOK_SECRET;

if (!verifyWebhook(req.body, signature, secret)) {
return res.status(401).send("Invalid signature");
}

// Process webhook
console.log("Event:", req.body.event);
res.status(200).send("OK");
});

Python:

import hmac
import hashlib

from flask import Flask, request, abort

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-DotVault-Signature', '')
secret = os.environ['DOTVAULT_WEBHOOK_SECRET']

expected = 'sha256=' + hmac.new(
secret.encode(),
request.data,
hashlib.sha256
).hexdigest()

if not hmac.compare_digest(signature, expected):
abort(401)

payload = request.get_json()
print(f"Event: {payload['event']}")

return 'OK'

Retry Logic

DotVault retries failed webhooks:

AttemptDelay
1Immediate
25 seconds
325 seconds
42 minutes
510 minutes

Webhooks disabled after 5 consecutive failures.

Testing Webhooks

Via API:

POST /v1/projects/{projectId}/webhooks/{webhookId}/test

Response:
{
"data": {
"success": true,
"responseStatus": 200,
"responseTime": 245
}
}

Via CLI:

dotvault webhook test my-project --webhook-id whk_xxx

Using webhook.site:

  1. Visit https://webhook.site
  2. Copy unique URL
  3. Create webhook with that URL
  4. Trigger test event
  5. Inspect payload on webhook.site

SDKs

JavaScript/TypeScript

npm install @dotvault/sdk
import { DotVault } from "@dotvault/sdk";

const dotvault = new DotVault({
apiKey: "dv_live_...",
});

// Get environment
const env = await dotvault.envs.get("proj_xxx", "env_xxx");
console.log(env.content);

// Update environment
await dotvault.envs.update("proj_xxx", "env_xxx", {
content: "DATABASE_URL=postgres://...",
});

// List projects
const projects = await dotvault.projects.list();

Python

pip install dotvault
from dotvault import DotVault

dotvault = DotVault(api_key='dv_live_...')

# Get environment
env = dotvault.envs.get('proj_xxx', 'env_xxx')
print(env.content)

# Update environment
dotvault.envs.update('proj_xxx', 'env_xxx', content='DATABASE_URL=postgres://...')

# List projects
projects = dotvault.projects.list()

Go

go get github.com/dotvault/go-sdk
package main

import (
"context"
"fmt"
"github.com/dotvault/go-sdk"
)

func main() {
client := dotvault.New("dv_live_...")

// Get environment
env, err := client.Envs.Get(context.Background(), "proj_xxx", "env_xxx")
if err != nil {
panic(err)
}
fmt.Println(env.Content)
}

Error Handling

HTTP Status Codes

CodeMeaning
200Success
201Created
400Bad Request
401Unauthorized
403Forbidden
404Not Found
409Conflict
429Rate Limited
500Server Error

Error Codes

CodeDescription
VALIDATION_ERRORInvalid request parameters
UNAUTHORIZEDAuthentication required
FORBIDDENInsufficient permissions
NOT_FOUNDResource not found
CONFLICTResource already exists
RATE_LIMITEDToo many requests
INTERNAL_ERRORServer error

Retry Strategy

import time
from dotvault import DotVault, RateLimitError

dotvault = DotVault(api_key='dv_live_...')

def with_retry(func, max_retries=3):
for attempt in range(max_retries):
try:
return func()
except RateLimitError as e:
if attempt == max_retries - 1:
raise
time.sleep(e.retry_after)
except Exception as e:
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt) # Exponential backoff

# Usage
result = with_retry(lambda: dotvault.envs.get('proj_xxx', 'env_xxx'))

Best Practices

1. Use API Keys for Server-to-Server

API keys are simpler and more reliable than OAuth for backend services.

2. Scope API Keys

Create separate keys for different purposes:

# CI/CD deployments
dotvault api-key create --name "CI/CD" --scopes "read:envs"

# Monitoring
dotvault api-key create --name "Monitoring" --scopes "read:projects"

3. Handle Rate Limits

Implement exponential backoff:

async function withRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.status === 429) {
const delay = Math.pow(2, i) * 1000;
await new Promise((r) => setTimeout(r, delay));
} else {
throw error;
}
}
}
}

4. Verify Webhook Signatures

Always verify webhook signatures to ensure authenticity.

5. Use Idempotency Keys

For critical operations:

POST /v1/projects/{projectId}/envs
{
"label": "production",
"content": "...",
"idempotencyKey": "unique-key-123"
}

6. Implement Health Checks

GET /v1/health

Response:
{
"status": "healthy",
"version": "1.2.3",
"timestamp": "2024-01-15T10:30:00Z"
}

Support