links.arjun.tv/docs/end-user/best-practices/saas-b2b

SaaS and B2B

Best practices for SaaS growth teams, B2B marketers, and demand-gen operators using 10x to capture leads, run lifecycle retargeting, and govern multi-team link operations.

Business problems

  1. Lead attribution breaks across channels — A prospect clicks an ad, reads a blog post, then signs up on a different session. The original source is lost.
  2. No lifecycle retargeting without heavy tooling — Triggering behavior-based follow-ups requires integrating 3-4 tools (analytics, email, CRM, link management).
  3. Campaign setup bottleneck — Every new campaign requires coordination between marketing, ops, and engineering to wire up tracking.
  4. Multi-team link chaos — Multiple teams create links with no naming conventions, overlapping slugs, and no audit trail.
  5. Custom domain delays — Enterprise clients need branded short domains, but DNS setup and verification stall launches.

Feature-to-problem map

Problem10x featureGuide
Broken attributionctx token with session binding + conversion APIContext Token Lifecycle
No lifecycle toolingChain signals + prefetch + webhook deliveryChain Signals and Prefetch
Campaign setup bottleneckCampaign API with LEAD_CAPTURE goal + auto-defaultsCampaign Funnel Workflow
Multi-team link chaosCollaborators with role hierarchy + audit eventsCollaborators and Ownership
Custom domain delaysDomain reconciliation APICustom Domains

Recommended workflows

1. Set up a lead-capture campaign with one API call

Create a campaign with lead-capture defaults:

POST /v2/handles/{handle}/campaigns
{
  "campaignId": "webinar-q3",
  "goalType": "LEAD_CAPTURE",
  "status": "ACTIVE",
  "primaryLink": {
    "slug": "webinar",
    "destinationUrl": "https://app.example.com/webinar-signup",
    "title": "Q3 Product Webinar"
  }
}

The LEAD_CAPTURE goal auto-sets the tracking preset to ga4_lead and the conversion event to lead. No manual configuration needed.

On your signup success page, fire the conversion:

POST /v2/public/conversions
{
  "ctx": "<token from redirect>",
  "eventType": "lead",
  "idempotencyKey": "signup-user@example.com"
}

Use the email as the idempotency key to prevent duplicate lead records from form resubmissions.

2. Route traffic by device and segment

B2B traffic behaves differently on mobile vs. desktop. Route desktop users to the full demo page and mobile users to a lighter signup form:

PUT /v2/handles/{handle}/links/webinar
{
  "destinationUrl": "https://app.example.com/webinar-signup",
  "routingRules": [
    {
      "priority": 10,
      "destinationUrl": "https://app.example.com/webinar-signup?view=desktop",
      "conditions": { "deviceIn": ["DESKTOP"] }
    },
    {
      "priority": 20,
      "destinationUrl": "https://app.example.com/webinar-signup?view=mobile",
      "conditions": { "deviceIn": ["MOBILE", "TABLET"] }
    }
  ]
}

Combine with segment targeting to show returning visitors a different experience:

{
  "priority": 5,
  "destinationUrl": "https://app.example.com/webinar-returning",
  "conditions": { "segmentIn": ["engaged_prospect"] }
}

The engaged_prospect segment is auto-derived when a visitor's engagement score reaches 0.75 or higher based on accumulated click history.

3. Retarget prospects with chain signals

When a prospect visits your pricing page but does not convert, record a signal:

POST /v2/public/chain/signals
{
  "handle": "yourapp",
  "sessionId": "sig_yourapp_1740500000000_abc123",
  "eventType": "pricing_view",
  "signals": [
    { "signalKey": "page", "signalValue": "pricing", "confidence": 1.0 },
    { "signalKey": "plan_interest", "signalValue": "pro", "confidence": 0.8 }
  ]
}

Create a chain rule to show targeted messaging when they visit again:

{
  "ruleId": "pricing-followup",
  "enabled": true,
  "priority": 100,
  "triggerEvent": "page_view",
  "chainConditions": {
    "require": [
      { "signalKey": "page", "operator": "equals", "signalValue": "pricing" },
      { "signalKey": "plan_interest", "operator": "exists" }
    ],
    "exclude": [
      { "signalKey": "signup_complete", "operator": "exists" }
    ],
    "maxChainAgeMinutes": 10080
  },
  "action": {
    "type": "show_banner",
    "template": "Still evaluating the {{plan_interest}} plan? Book a 15-min demo."
  }
}

The 10,080-minute window (7 days) ensures the rule only fires for recent pricing page visitors.

Use prefetch to pre-compute decisions for multiple events in a single call:

POST /v2/public/chain/prefetch
{
  "handle": "yourapp",
  "sessionId": "sig_yourapp_1740500000000_abc123",
  "triggerEvents": ["page_view", "exit_intent", "idle_60s"]
}

The response includes a decision for each event, cached for 60 seconds. Your client-side code can act on the right one without additional API calls.

4. Govern multi-team link operations

Add team members with appropriate roles:

  • CREATOR — Marketing managers who create and manage links and campaigns.
  • OPERATOR — Ops leads who manage webhooks, PAT tokens, and audit events.
  • OWNER — Account admin who controls billing, collaborators, and ownership transfer.

Use the collaborators API to manage team members. The endpoint accepts a full array of collaborators (this is a replacement, not a patch):

PUT /v2/handles/{handle}/collaborators
[
  { "userId": "user_abc123", "role": "OPERATOR" },
  { "userId": "user_def456", "role": "CREATOR" }
]

Monitor changes via audit events:

GET /v2/handles/{handle}/audit-events

Audit events are retained for 90 days and cover collaborator changes, link imports, ownership transfers, and billing updates.

Set up webhooks to notify your team Slack channel on important events:

POST /v2/handles/{handle}/webhooks
{
  "endpointUrl": "https://hooks.slack.com/services/T.../B.../xxx",
  "eventTypes": ["campaign.health_changed", "destination_domain.blocked"]
}

5. Set up a custom branded domain

Add your domain and trigger reconciliation:

POST /v2/account/domains
{ "domain": "go.example.com" }

After adding the required DNS records (CNAME or A), trigger verification:

POST /v2/account/domains/go.example.com/reconcile

Reconciliation is asynchronous. Poll the domain status until it shows ready. This may take multiple attempts as DNS propagation completes.

Once ready, your links resolve at https://go.example.com/{slug} instead of the default 10x.in subdomain.

Key metrics to track

MetricWhere to find itWhat it tells you
Lead volumeGET /v2/handles/{handle}/analytics?funnel=true&campaignId={id}Conversions per campaign
Source attributionGET /v2/handles/{handle}/analytics?groupBy=referrer&campaignId={id}Which channels drive leads
Device splitGET /v2/handles/{handle}/analytics?groupBy=deviceDesktop vs. mobile conversion
Retargeting effectivenessChain signal depth + conversion after signalWhether lifecycle rules recover leads
Data confidenceFunnel response confidence fieldReliability of attribution data

Common mistakes

  1. Using raw clicks for conversion rate. B2B traffic often has bot noise from security scanners and link previews. Always use qualifiedClicks for accurate rates.
  2. Not separating campaigns by channel. Create distinct campaigns for paid, organic, email, and partner channels. Mixing them makes attribution meaningless.
  3. Setting chain rule windows too short. B2B sales cycles are long. A 24-hour window misses most prospects. Use 7-14 day windows (maxChainAgeMinutes: 10080-20160).
  4. Giving all team members OWNER role. Follow the principle of least privilege. Most team members need CREATOR, not OWNER. Reserve OWNER for account administrators.
  5. Starting domain reconciliation before DNS propagation. Wait at least 15 minutes after adding DNS records before triggering reconciliation. Premature attempts will fail.

Related: