Last updated · May 2026
- Production-ready APIs
- SOC 2 Type I - In Progress
- @datavibe.cc/sdk
- pip install datavibe
- REST API
- Agentic AI
Integration guide
Live API—https://api.datavibe.cc/v1/gate/outbound
Govern every AI output with the official SDK — or POST to the Gate directly. Policy scan, human review queue, and tamper-evident audit on every action.
Official SDK (recommended)
Install the SDK for retries, typed errors, and a single check() call. Your LLM API keys stay on your infrastructure — only the generated text reaches DataVibe.
npm install @datavibe.cc/sdkpip install datavibeErgonomic alias: createGate(apiKey) (TypeScript) or create_gate(api_key) (Python). Runnable samples in the examples/ folder.
Generate keys at Security → API Keys.
streamCheck() — agentic token streams
For autonomous agents that stream tokens, send incremental chunks to POST /v1/gate/stream. The gate buffers text, runs deterministic policy on each flush, and returns pending until a violation or final: true.
// Agentic streaming — flush chunks as the model generates
let sessionId: string | undefined;
for await (const chunk of openaiStream) {
const res = await dv.streamCheck({
content: chunk,
sessionId,
chunkIndex: sessionId ? undefined : 0,
final: false,
});
sessionId = res.session_id;
if (res.verdict === "blocked") {
throw new Error("Governance blocked mid-stream");
}
}
// Final flush
await dv.streamCheck({ content: "", sessionId, final: true });check() — output-only governance
You call your own LLM. Send only the output to DataVibe. Use contentType: "agent_action" for tool calls and business actions; email for outbound messages.
TypeScript
import { DataVibeClient } from "@datavibe.cc/sdk";
const dv = new DataVibeClient({ apiKey: process.env.DATAVIBE_API_KEY! });
const verdict = await dv.check({
content: modelOutput,
contentType: "agent_action", // email | message | tool_call | api_response | …
});
if (verdict.verdict === "blocked") {
throw new Error(`Blocked: ${verdict.violations.map((v) => v.rule).join(", ")}`);
}
if (verdict.verdict === "review_required") {
return { status: "held", reviewUrl: verdict.reviewUrl };
}
// safe — proceedPython
from datavibe import DataVibeClient
dv = DataVibeClient(api_key="dv_live_…")
verdict = dv.check(
"Agent proposed issuing a $500 credit without approval",
content_type="agent_action",
)
if verdict.verdict == "blocked":
raise RuntimeError("Governance BLOCK")
if verdict.verdict == "review_required":
return {"hold": verdict.review_url}generateAndCheck() — governed generation
DataVibe generates and governance-checks in one call (BYOK or shared provider). See dashboard Settings → AI Provider for mode.
const result = await dv.generateAndCheck({
prompt: "Draft a follow-up for a churn-risk account",
contentType: "email",
});
// result.verdict + result.content when safeLangChain / LangGraph integration →
MCP server (Cursor, Claude Code)
Expose governance tools to MCP-compatible clients. Set DATAVIBE_API_KEY in your MCP config.
{
"mcpServers": {
"datavibe": {
"command": "npx",
"args": ["-y", "@datavibe/mcp-server"],
"env": { "DATAVIBE_API_KEY": "dv_live_…" }
}
}
}Package: @datavibe/mcp-server. Publish runbook: monorepo docs/sdk-publish.md.
HTTP reference (outbound email)
Prefer raw HTTP? The examples below use POST /v1/gate/outbound for email workflows. Agent and multi-channel actions use the same governance primitives via SDK or /v2/gate/outbound.
How it fits in your stack
Your current outbound stack probably looks like this:
Clay / OpenAI / custom script → Resend / SendGrid / Apollo → Prospect inboxThe DataVibe Gate intercepts between generation and dispatch:
Clay / OpenAI / custom script → DataVibe Gate → Human approval → Resend → Prospect inboxOne line of code changes. Everything else stays the same.
Authentication
Every request to the Gate API requires a dv_live_* API key in the Authorization header. Generate one from Security → API Keys in the dashboard. Keys are workspace-scoped — submissions are only visible to members of your workspace.
1. Submit a message to the Gate
Replace your current "send email" call with a single POST to /v1/gate/outbound:
curl -X POST https://api.datavibe.cc/v1/gate/outbound \
-H "Authorization: Bearer dv_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipient": "[email protected]",
"subject": "Quick question regarding your infrastructure",
"body_html": "<p>Hi there, I noticed...</p>",
"source_model": "gpt-4o",
"campaign_id": "outbound_q3"
}'Request fields
| Field | Required | Description |
|---|---|---|
recipient | Yes | Recipient email address. |
subject | Yes | Email subject line. |
body_html | Yes | Email body as HTML. |
body_text | No | Plain-text fallback. Improves deliverability. |
source_model | No | LLM model that generated this (e.g. gpt-4o). Logged for analytics. |
campaign_id | No | Links this submission to a campaign for conversion tracking. |
metadata | No | Any extra JSON context — score, template name, etc. |
idempotency_key | No | Re-submitting the same key returns the original action_id rather than creating a duplicate. |
2. Handle the response
The Gate scans your payload synchronously before responding. If it passes all automated policy checks, it enters your human approval queue:
{
"action_id": "gov_8f72c91a",
"status": "queued",
"policy_passed": true,
"policy_violations": [],
"review_url": "https://app.datavibe.cc/queue?action=gov_8f72c91a",
"message": "Submission queued for human review. Approve at the review_url."
}If the policy engine detects a hard violation — hallucinated pricing, profanity, fake guarantees — the submission is rejected immediately and never enters the queue. Fix the payload and resubmit:
{
"action_id": "gov_3c12d04f",
"status": "BLOCKED",
"policy_passed": false,
"policy_violations": [
{
"rule": "pricing_hallucination",
"severity": "BLOCK",
"detail": "Pricing or discount claim detected: '30% off'. LLMs frequently hallucinate pricing — this cannot be auto-approved."
}
],
"review_url": null,
"message": "Submission blocked by policy engine. Fix and resubmit."
}Pro tip
Route the review_url to a dedicated Slack channel so your SDR manager can approve outbound drafts directly from their phone — without logging into the dashboard.
3. Poll for execution status
Once queued, poll the status endpoint to know when your message was sent (or rejected):
curl https://api.datavibe.cc/v1/gate/outbound/gov_8f72c91a \
-H "Authorization: Bearer dv_live_YOUR_API_KEY"{
"action_id": "gov_8f72c91a",
"status": "SENT",
"sent_at": "2026-05-12T10:43:22Z",
"provider_message_id": "re_123abc456",
"reviewed_at": "2026-05-12T10:43:18Z"
}Status values
| Status | Meaning |
|---|---|
QUEUED | Policy scan passed. Waiting for human approval. |
BLOCKED | Policy engine hard-blocked. Submission rejected at the gate — never queued. |
APPROVED | Approved by a human. Execution in progress. |
SENT | Email dispatched successfully. provider_message_id is the Resend message ID. |
FAILED | Dispatch failed after approval. Check send_error for details. |
REJECTED | Rejected by a human reviewer. Discard and regenerate. |
4. List submissions
Fetch pending or historical submissions for your workspace. Useful for building your own queue UI or syncing status to a CRM:
curl "https://api.datavibe.cc/v1/gate/submissions?status=QUEUED&limit=20" \
-H "Authorization: Bearer dv_live_YOUR_API_KEY"Filter by status=QUEUED, SENT, REJECTED, or BLOCKED. Default limit is 50, maximum 200.
Python drop-in example
A drop-in replacement for whatever function currently calls your sending infrastructure. No SDK required — just the standard requests library:
import requests
def send_via_gate(recipient: str, subject: str, body_html: str, campaign_id: str | None = None):
"""Drop-in replacement for your current email send function."""
response = requests.post(
"https://api.datavibe.cc/v1/gate/outbound",
json={
"recipient": recipient,
"subject": subject,
"body_html": body_html,
"source_model": "gpt-4o", # or whichever model generated this
"campaign_id": campaign_id,
},
headers={"Authorization": "Bearer dv_live_YOUR_API_KEY"},
timeout=10,
)
response.raise_for_status()
data = response.json()
if data["status"] == "BLOCKED":
# Policy hard-blocked — log violations and regenerate
raise ValueError(f"Gate blocked: {data['policy_violations']}")
# QUEUED — notify your team
print(f"Queued for review: {data['review_url']}")
return data["action_id"]What the policy engine checks
Every submission is scanned synchronously before entering the queue. Violations are either BLOCK (hard stop — submission rejected) or WARN (flagged for the reviewer but still queued).
| Rule | Severity | What it catches |
|---|---|---|
competitor_mention | WARN | Named competitors in subject or body. |
pricing_hallucination | BLOCK | Discount %, pricing claims, promo language. LLMs invent these constantly. |
fake_guarantee | BLOCK | Guarantee, money-back, warranty language. Creates legal liability. |
spam_trigger_phrase | WARN | Classic spam-filter bait that hurts deliverability. |
profanity | BLOCK | Profanity in subject or body. |
all_caps_phrase | WARN | Four or more consecutive ALL-CAPS words — spam signal. |
excessive_exclamation | WARN | More than 3 exclamation marks. |
forbidden_attachment_ref | BLOCK | Claims an attachment exists. The gate does not dispatch attachments. |
unsubscribe_missing | WARN | No opt-out language. May violate CAN-SPAM / GDPR. |
suspicious_url_pattern | WARN | URL shorteners (bit.ly, tinyurl, etc.) reduce deliverability. |
Custom rules per workspace — including your own competitor list, forbidden phrases, and required disclosures — are on the roadmap for Phase 2.