Fourbyfour
WorkflowsDocs
Try Playground
PricingWorkflowsDocsAboutBlogCareerSupportDiscordPrivacyTerms

© 2026 Occupy Mars Private Limited. All rights reserved.

Getting Started

SDK

Platform

DocsIntroduction

Recover & Grow Subscription Revenue

Track events. We handle the rest. Pre-built workflows recover payments, convert trials, and retain subscribers—optimized across thousands of subscription businesses.

How it works

1

Track events

Call fbf.track() when things happen

2

Workflows trigger

Pre-built workflows match your events

3

Users notified

Right message, right channel, right time

4

Revenue recovered

Payments saved, trials converted, subscribers retained

Subscription Workflows

12 intelligent workflows, each optimized for subscription revenue recovery and growth.

Involuntary Churn Prevention

Proactive outreach before payment fails completely

payment.failing

Payment Recovery

Multi-step recovery adapts to failure reason and customer value

payment.failed

Trial Activation

Guide to activation based on what they haven't tried yet

trial.started

Trial Conversion

Convert based on usage depth and activation score

trial.ending
View all 12 workflows

Installation

Install the SDK and start recovering revenue in minutes.

1. Install the SDK

1npm install @fourbyfour/sdk
TypeScript
Ln 1UTF-8

2. Initialize with your API key

123456import Fourbyfour from '@fourbyfour/sdk'; const fbf = new Fourbyfour({  apiKey: process.env.FOURBYFOUR_API_KEY,  projectId: process.env.FOURBYFOUR_PROJECT_ID});
TypeScript
Ln 6UTF-8

Get your API key from the dashboard. 12 pre-built workflows optimized for subscription revenue recovery are ready to use.

Quick Start

Two functions. That's all you need.

Select an event

fbf.track()

→ triggers Payment Recovery workflow
12345678// Payment failed → triggers recovery workflowawait fbf.track('payment.failed', {  userId: 'cus_9s6XGrhLkFm2pQ',  plan: 'Pro',  amount: 99,  currency: 'USD',  failureReason: 'card_declined'});
TypeScript
Ln 8UTF-8

fbf.notify()

→ helps us optimize delivery
1234567// Send context to help optimize deliveryawait fbf.notify({  userId: 'cus_9s6XGrhLkFm2pQ',  timezone: 'America/New_York',  preferredChannel: 'email',  tier: 'premium'});
TypeScript
Ln 7UTF-8

What happens next?

When you call track('payment.failed'), our Payment Recovery workflow kicks in automatically. We handle timing, channel selection, message optimization—all tuned from patterns across thousands of subscription businesses.

AI Integration Generator

Answer a few questions about your stack and we'll generate a step-by-step integration guide that you can feed to your AI coding assistant (Claude Code, Cursor, Copilot, etc.).

What's your tech stack?

We'll generate code examples in your language.

How to use the generated guide:

  1. Complete the wizard above to generate your integration guide
  2. Copy the guide or download as a markdown file
  3. Open your AI coding assistant (Claude Code, Cursor, etc.)
  4. Paste the guide and ask it to "implement this integration"
  5. Replace YOUR_PROJECT_ID and YOUR_API_KEY with your actual values from the dashboard
  6. Review the changes and commit

Two Functions

Two functions. No PII. That's the entire SDK.

fbf.track(event, payload)

Events that trigger workflows

1234567891011121314151617181920import Fourbyfour from '@fourbyfour/sdk'; const fbf = new Fourbyfour({  apiKey: process.env.FOURBYFOUR_API_KEY,  projectId: process.env.FOURBYFOUR_PROJECT_ID}); // Minimum: just userId (we use defaults)await fbf.track('payment.failed', {  userId: 'cus_9s6XGrhLkFm2pQ'}); // Full: everything you know (better optimization)await fbf.track('payment.failed', {  userId: 'cus_9s6XGrhLkFm2pQ',  plan: 'Pro',  amount: 99,  currency: 'USD',  failureReason: 'card_declined'});
TypeScript
Ln 20UTF-8
fbf.notify(context)

Context that helps us optimize

123456789// Send context anytime to help us optimize deliveryawait fbf.notify({  userId: 'cus_9s6XGrhLkFm2pQ',  timezone: 'America/New_York',  // When to reach them  tier: 'premium',               // Priority signals  preferredChannel: 'email',     // Channel preference  language: 'en',                // Localization  ltv: 1200                      // Value signals});
TypeScript
Ln 9UTF-8

Data Philosophy

Minimum (just userId)

Works out of the box. We use intelligent defaults optimized across thousands of companies.

123await fbf.track('payment.failed', {  userId: 'cus_9s6XGrhLkFm2pQ'});
TypeScript
Ln 3UTF-8

Full (everything you know)

Better optimization. More context = smarter timing, channels, and messaging.

1234567await fbf.track('payment.failed', {  userId: 'cus_9s6XGrhLkFm2pQ',  plan: 'Pro',  amount: 99,  currency: 'USD',  failureReason: 'card_declined'});
TypeScript
Ln 7UTF-8

The key: Send what you have, when you have it. No need to block on missing fields. We fill gaps with smart defaults, then improve as you send more.

What Goes Where

trackNo PII

Business events: amount, plan, cartTotal

notifyNo PII

User context: timezone, tier, preferences

resolverTransient

Delivery info: email, phone, name

FunctionData TypeExamplePII?
trackBusiness eventsamount, plan, cartTotalNo
notifyUser contexttimezone, tier, preferencesNo
resolverDelivery infoemail, phone, nameYes (transient)

Notice: PII only flows through your resolver endpoint, fetched just-in-time, used for delivery, never stored. See the Resolver section.

Resolver

Just-in-time PII. Your data stays in your system until the moment of delivery.

How It Works

1

Workflow triggers

User event matches a workflow

2

We call your resolver

Request only needed fields

3

You return PII

Email, phone, name: what's needed

4

We deliver & discard

Used for send, never stored

Request & Response

We send:

{}JSON
1234567891011POST /api/fourbyfour/resolveHeaders:  x-fbf-signature: sha256=... Body:{  "userId": "cus_9s6XGrhLkFm2pQ",  "fields": ["email", "name"],  "workflowId": "payment-recovery",  "channel": "email"}
JSON
Ln 11UTF-8

You respond:

{}JSON
123456789101112HTTP 200 OK {  "email": "user@example.com",  "name": "John"} // Or if user not found:HTTP 404 Not Found{  "error": "User not found"}
JSON
Ln 12UTF-8

Implementation

12345678910111213141516171819202122232425// Express / Next.js API routeapp.post('/api/fourbyfour/resolve', async(req, res) => {  // Verify the request is from Fourbyfour  const signature = req.headers['x-fbf-signature'];  if(!verifySignature(signature, req.body, process.env.FBF_WEBHOOK_SECRET)) {    return res.status(401).json({ error: 'Invalid signature' });  }   const { userId, fields, workflowId, channel } = req.body;   // Fetch user from your database  const user = await db.users.findById(userId);  if(!user) {    return res.status(404).json({ error: 'User not found' });  }   // Return only requested fields  const response: Record<string, string> = {};   if(fields.includes('email')) response.email = user.email;  if(fields.includes('name')) response.name = user.firstName;  if(fields.includes('phone')) response.phone = user.phone;   res.json(response);});
TypeScript
Ln 25UTF-8

Fields by Channel

Email
email+ name
SMS
phone+ name
WhatsApp
phone+ name
Push
pushToken+ name, device
ChannelRequiredOptional
Emailemailname
SMSphonename
WhatsAppphonename
PushpushTokenname, device

Why This Architecture

Privacy by Design

  • • PII never leaves your system until needed
  • • We request only what the channel requires
  • • Data used for delivery, then discarded

Always Fresh

  • • No stale emails or phones
  • • User updates their info? Next send has it
  • • User deletes account? Returns 404, we skip

You Stay in Control

  • • Return only what you want to share
  • • Add business logic (block certain users)
  • • GDPR compliant by default

Workflows

12 subscription revenue workflows. Track the event, we handle timing, channels, and intelligent messaging.

How it works

1

Track event

Call client.track()

2

Workflow triggers

Matches your event

3

AI optimizes

Timing, tone, offers

4

Revenue recovered

Payments, trials, churn

Try all workflows in the playground

Channels

We deliver via the optimal channel. You just provide the resolver endpoint. We handle the rest.

Email

Best for: detailed info, rich content

email

SMS

Best for: urgent, time-sensitive

phone

WhatsApp

Best for: conversational, global

phone

Push

Best for: real-time, mobile

pushToken

How We Select Channels

1

User Preference

If you sent preferredChannel via notify(), we start there.

2

Context Analysis

We consider timezone, urgency, past engagement, and message type.

3

Cross-org Learning

We know "users in this segment convert 2x better via WhatsApp".

What We Optimize

Timing→ Send at 10am local

User's timezone is IST

Channel→ Use SMS over email

Past engagement higher on SMS

Frequency→ Wait before next

User received message yesterday

Urgency→ Escalate to SMS

Payment fails in 24h

SignalExampleImpact
TimingUser's timezone is ISTSend at 10am local time
ChannelPast engagement higher on SMSUse SMS over email
FrequencyUser received message yesterdayWait before next touch
UrgencyPayment fails in 24hEscalate to SMS

Automatic Fallbacks

If a channel fails or isn't available, we automatically try the next best option:

Email fails→Try SMS→Try Push