Developer documentation
Pulse follows one rule: Capture → Analyze → Discard Media → Store Insight. Raw images, video and audio are never stored.
1. Install the widget
<script src="https://pulse.etiquetteai.in/widget/pulse.js" data-site-key="YOUR_PUBLIC_SITE_KEY" async></script>
Add your domain under Sites and verify it (DNS TXT, meta tag, or well-known file). The widget only works on allow-listed origins. For inline mode place <div data-pulse-target></div> where it should render. The widget runs in a Shadow DOM, is under 14 KB gzipped, and loads the camera/microphone module only after the visitor clicks I understand and continue.
2. Authentication
Dashboard calls use a session cookie plus CSRF (GET /api/v1/auth/csrf). Select an organization with X-Pulse-Org. Server integrations use a bearer token:
Authorization: Bearer etp_xxxxxxxx # organization API key, scoped
Authorization: Bearer sk_xxxxxxxx # per-site secret key (read-only analytics/feedback)
Keys are shown once, stored hashed, and can be rotated at POST /api/v1/api-keys/{id}/rotate.
3. REST API (/api/v1)
| Area | Endpoints |
|---|---|
| Auth / account | auth/csrf, auth/register, auth/login, auth/logout, me, organization, audit |
| Team | team/members[/{id}], team/invitations, team/invitations/accept (roles: owner, admin, analyst, developer, billing) |
| API keys | api-keys, api-keys/{id}, api-keys/{id}/{action} |
| Sites | sites, sites/{id}, sites/{id}/{action} (verify, rotate keys…), widget-config/{site_id} |
| Feedback | feedback (filters: site, page_url, rating, sentiment, date range), feedback/{id} |
| Analytics / insights | analytics, analytics/{section}, insights, alert-events |
| Alerts / webhooks | alerts, webhooks, webhooks/{id}/deliveries |
| Attributes | attributes, industries |
| Reviews (tenant) | reviews, reviews/{id}, reviews/{id}/{action} – respond, flag, resolve. Businesses can flag but never remove reviews. |
| Billing | plans (public), subscriptions, billing, billing/webhooks/razorpay |
| Platform staff | admin/revenue, admin/organizations, admin/plans, admin/coupons, admin/attributes, admin/scoring-methods, admin/moderation, admin/security-events, admin/system-health |
Responses are JSON, paginated with page/count/results. Errors use {"error":{"code","message"}}. Rate limits return HTTP 429.
Widget API (public, origin allow-listed)
GET /api/widget/v1/config, POST /api/widget/v1/sessions (records consent metadata only), POST /api/widget/v1/feedback (multipart; media is analyzed in memory and discarded).
Public directory API
GET /api/public/v1/companies[/{slug}[/reviews]] (sort: useful, recent, highest, lowest, detailed), visitor-token, and reviews/{id}/vote|report|confirm-resolution.
4. Webhooks
Create endpoints at POST /api/v1/webhooks (HTTPS, public addresses only). Each delivery carries X-Pulse-Signature: t=<unix>,v1=<hex> where the hex value is HMAC-SHA256 of "<t>." + raw body with your endpoint secret. Reject timestamps older than 5 minutes. Failed deliveries are retried with backoff and visible under deliveries.
import hmac, hashlib, time
def verify(secret, body: bytes, header: str, tolerance=300):
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > tolerance: return False
expected = hmac.new(secret.encode(), parts["t"].encode() + b"." + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])
5. Privacy architecture
Uploads use a memory-only handler, there is no media storage directory, buffers are zeroed after analysis (also on errors), Nginx logs only the URI, request bodies are buffered in RAM, and no media is sent to Celery or logged. Consent is stored as metadata (type, version, time). If the AI step fails, media is discarded and the visitor can still submit a rating and text. A critical automated test (test_raw_media_deletion.py) proves no raw media bytes remain in the database, cache, logs or temp directories.
6. Attributes
Platform staff add attributes at Admin → Attributes (or POST /api/v1/admin/attributes) and attach them to industry profiles. No code or schema change is needed. AI assessments must quote evidence from the customer's own text; otherwise the state is insufficient_information and no score is invented. Scoring methods are versioned.
7. Known limitations
The built-in rule-based provider cannot analyze images (visible engagement is "not assessed"); enable the Anthropic provider for image-frame analysis. Voice uses the browser's transcript when available. "Video" means a few sampled frames, not a video file. SSO and Slack/Teams alerts are extension points. Live Razorpay and Anthropic calls need your credentials and should be tested in staging.