AI SECURITY TEAM

Integration guide

Deploying the team

Every deployment gets two keys from the owner. A public key for the browser agent — locked to the client’s registered domains — and a secret server key for sensitive events like logins, transfers and data exports. Events are rejected until the owner activates the deployment.

1 · Websites: add the browser agent

Paste before </body>. It reports page views and scans form submissions for injection attacks and exposed card/ID numbers. Form contents are inspected on our side and immediately discarded.

index.html
<script src="https://your-team.netlify.app/agent.js" data-key="ast_pub_…" defer></script>

Report login outcomes from the page too, if your login happens client-side:

AISecurityTeam.track('auth.login_failed', { actorId: 'user@example.com' })

2 · Servers: send events to the API

Call from your backend whenever something security-relevant happens. Batch up to 100 events per request. Keep the secret key on the server only.

curl
curl -X POST https://your-team.netlify.app/api/v1/events \
  -H "Authorization: Bearer ast_sec_…" \
  -H "Content-Type: application/json" \
  -d '{"type":"payment.transfer","actorId":"user_42","amount":2500,"target":"payee_981","ip":"203.0.113.7"}'
Node.js
await fetch('https://your-team.netlify.app/api/v1/events', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.AI_SECURITY_TEAM_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    events: [
      { type: 'auth.login_failed', actorId: user.id, ip: req.ip },
      { type: 'http.request', ip: req.ip, path: req.originalUrl, userAgent: req.get('user-agent') },
    ],
  }),
})

Event reference

typeuseful fieldswho watches
auth.login_failedactorId, ipSentinel: brute force, credential stuffing
auth.login_successactorId, ip, country, deviceIdWatchtower: new-country and new-device logins
auth.password_resetactorId, ipSentinel + Ledger: account-takeover signals
account.security_changeactorId, changeSentinel: MFA, email, phone, recovery changes
account.privilege_changeactorId, newRoleSentinel: privilege escalation
payment.payee_addedactorId, targetLedger: fresh-payee fraud
payment.transferactorId, amount, targetLedger: thresholds, velocity, anomalies
data.exportactorId, records, targetVault: bulk exports
data.accessactorId, targetVault: scraping
http.requestip, path, userAgentWatchtower: floods, probes, attack tools
input.submittedpayload, pathSentinel + Vault: injection & PII (payload is never stored)
page_viewpathWatchtower: request floods (browser agent)

All fields are optional except type. Common fields: actorId, ip, country (ISO code), userAgent, amount, target, records, path, payload. Send sessionId and deviceId so the Investigator can link one person's activity across agents. change is one of mfa_disabled, mfa_enabled, email_changed, phone_changed, password_changed, recovery_changed, api_key_created. Server events may set occurredAt (ISO time, up to 30 days old) to backfill history.

3 · Alerts

New incidents are posted to the deployment’s alert webhook the moment they’re detected. The message format works with Slack and Discord incoming webhooks directly, and includes structured JSON for Zapier, Make or n8n — use those to forward alerts to email or SMS. The owner also receives every high and critical alert across all deployments.

Responses

  • 202 accepted — includes how many incidents were opened
  • 401 unknown or missing key · 403 deployment not active or origin not registered
  • 423 the owner has engaged the kill switch
  • 429 your organization or source IP exceeded its rate limit (see Retry-After) · 503 your organization’s queue is full, retry later
  • 200 duplicate — the Idempotency-Key header was already processed; per-event eventId values are also de-duplicated

4 · Enterprise APIs

Create a service account under Console → Organization. Keys (ast_svc_…) are scoped to one organization and shown once. Scopes: incidents:read, incidents:write, reports:read, audit:read, scim.

  • GET /api/v1/incidents?status=&severity=&since=&before=&limit=
  • GET /api/v1/incidents/:id · PATCH { status?, assigneeEmail?, note?, legalHold? }
  • GET /api/v1/reports/summary?days=30 · GET /api/v1/audit
  • SCIM 2.0 Users: /scim/v2/Users (Groups not supported)
  • SAML 2.0: /sso/saml/{org-slug}/metadata · /acs · /login

Signed webhook connectors send the ast.incident.v1 envelope with this header:

X-AST-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, t + "." + rawBody)>

// Node.js verification
const [t, v1] = header.split(',').map((p) => p.split('=')[1])
const expected = crypto.createHmac('sha256', WHSEC).update(`${t}.${rawBody}`).digest('hex')
const ok = v1?.length === expected.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected)) && Date.now() / 1000 - Number(t) < 300