Skip to main content
Grant signals notify the Encore backend when you’ve granted access to features. The SDK provides methods for sending grant signals and polling for advertiser verification.

didGrant()

Notify the backend when you’ve granted access to a feature. This is typically sent automatically by the SDK, but you can also send it manually for custom workflows.

Signature

function didGrant(
  access: 'provisional' | 'final',
  transactionId: string
): void

Parameters

access
string
required
Grant type: 'provisional' or 'final'
  • 'provisional': User completed offer action (sent automatically by SDK)
  • 'final': Publisher confirms delivery of entitlement (e.g., subscription activated)
transactionId
string
required
The transaction ID from the entitlement

Return Value

Type: void The method is fire-and-forget. Signals are queued if offline and sent when connectivity is restored.

Examples

import Encore from '@encore/web-sdk';

// After successfully activating subscription
Encore.didGrant('final', transactionId);

Grant Signal Behavior

  • Non-blocking: Fire-and-forget, doesn’t wait for response
  • Offline queue: Automatically queued in localStorage if offline
  • Auto-retry: Retries with exponential backoff (max 3 attempts)
  • Persistent: Stored until successfully delivered

waitForVerification()

Poll the API for advertiser verification of a conversion. Returns when verified or timeout is reached.

Signature

function waitForVerification(
  transactionId: string,
  timeout?: number
): Promise<VerificationStatus>

Parameters

transactionId
string
required
The transaction ID to check for verification
timeout
number
default:30000
Maximum time to wait in milliseconds. Default: 30000 (30 seconds)

Return Value

Type: Promise<VerificationStatus>
type VerificationStatus = 'pending' | 'verified' | 'failed' | 'timeout'
  • 'verified': Conversion confirmed by advertiser
  • 'pending': Still waiting for verification
  • 'failed': Verification failed
  • 'timeout': Polling timed out

Polling Behavior

The SDK polls with exponential backoff:
  • Initial interval: 1 second
  • Max interval: 8 seconds
  • Pattern: 1s → 2s → 4s → 8s → 8s → …
  • Auto-refresh: Refreshes entitlements when verified

Examples

Basic Verification Wait

import Encore from '@encore/web-sdk';

const result = await Encore.presentOffer();

if (result.granted) {
  // Wait for verification (30 seconds default)
  const status = await Encore.waitForVerification(
    result.entitlement.transactionId
  );
  
  if (status === 'verified') {
    console.log('Conversion verified!');
    showVerifiedBadge();
  } else {
    console.log('Still provisional');
  }
}

Custom Timeout

// Wait up to 60 seconds
const status = await Encore.waitForVerification(
  transactionId,
  60000  // 60 seconds
);

switch (status) {
  case 'verified':
    showSuccessMessage('Verified!');
    break;
  case 'timeout':
    showMessage('Still processing... Check back soon');
    break;
  case 'failed':
    showError('Verification failed');
    break;
  default:
    showMessage('Pending verification');
}

With Loading State

async function checkVerification(transactionId) {
  setLoading(true);
  setMessage('Waiting for verification...');
  
  try {
    const status = await Encore.waitForVerification(
      transactionId,
      60000
    );
    
    if (status === 'verified') {
      setMessage('Verified! Full access granted.');
      unlockFullFeatures();
    } else {
      setMessage('Still processing. You have provisional access.');
    }
  } finally {
    setLoading(false);
  }
}

Complete Workflow Example

import Encore from '@encore/web-sdk';

async function handleOfferFlow() {
  // 1. Present offer to user
  const result = await Encore.presentOffer();
  
  if (!result.granted) {
    console.log('User declined');
    return;
  }
  
  // 2. Provisional access granted immediately
  console.log('Provisional access granted');
  unlockFeature();  // Give instant access
  
  // 3. Optional: Send custom grant signal
  Encore.didGrant('final', result.entitlement.transactionId);
  
  // 4. Wait for advertiser verification
  showMessage('Verifying your offer...');
  
  const status = await Encore.waitForVerification(
    result.entitlement.transactionId,
    60000  // 60 seconds
  );
  
  // 5. Handle verification result
  if (status === 'verified') {
    console.log('Verified! Upgrading to full access');
    showVerifiedBadge();
    trackEvent('conversion_verified');
  } else {
    console.log('Still provisional, will verify later');
    showPendingBadge();
  }
}

Framework Integration

  • React
  • Vue
import { useState } from 'react';
import Encore from '@encore/web-sdk';

function OfferFlow() {
  const [status, setStatus] = useState('idle');
  const [message, setMessage] = useState('');
  
  const handleOffer = async () => {
    setStatus('presenting');
    
    const result = await Encore.presentOffer();
    
    if (!result.granted) {
      setStatus('declined');
      return;
    }
    
    // Provisional access
    setStatus('provisional');
    setMessage('Checking verification...');
    
    // Wait for verification
    const verifyStatus = await Encore.waitForVerification(
      result.entitlement.transactionId,
      60000
    );
    
    if (verifyStatus === 'verified') {
      setStatus('verified');
      setMessage('Verified! Full access granted.');
    } else {
      setStatus('provisional');
      setMessage('Access granted. Verification pending.');
    }
  };
  
  return (
    <div>
      <button onClick={handleOffer}>Get Premium</button>
      <p>{message}</p>
      {status === 'verified' && <VerifiedBadge />}
      {status === 'provisional' && <PendingBadge />}
    </div>
  );
}

Best Practices

1. Grant Immediate Access

Don’t wait for verification before unlocking features:
// Good: Instant access
const result = await Encore.presentOffer();
if (result.granted) {
  unlockFeature();  // ✅ Immediate
  waitForVerification(result.entitlement.transactionId);  // Background
}

// Avoid: Delayed access
const result = await Encore.presentOffer();
if (result.granted) {
  await waitForVerification(result.entitlement.transactionId);
  unlockFeature();  // ❌ User waits unnecessarily
}

2. Show Verification Status

Inform users about verification status:
if (Encore.isActive({ type: 'freeTrial' }, 'verified')) {
  showBadge('Verified');
} else if (Encore.isActive({ type: 'freeTrial' }, 'provisional')) {
  showBadge('Pending Verification');
}

3. Don’t Block on Verification

Poll in the background, don’t block UI:
// Good: Background polling
const result = await Encore.presentOffer();
if (result.granted) {
  unlockFeature();
  // Poll in background
  checkVerificationInBackground(result.entitlement.transactionId);
}

// Avoid: Blocking UI
const result = await Encore.presentOffer();
if (result.granted) {
  showLoadingSpinner();
  await Encore.waitForVerification(transactionId, 60000);  // ❌ Blocks for 60s
  unlockFeature();
}

4. Use Reasonable Timeouts

// Good: Reasonable timeout
await Encore.waitForVerification(transactionId, 60000);  // 60 seconds

// Avoid: Too long
await Encore.waitForVerification(transactionId, 300000);  // 5 minutes ❌

Offline Behavior

Grant signals are automatically handled when offline:
  1. Queued: Stored in localStorage when offline
  2. Sent: Automatically sent when connectivity restored
  3. Retried: Exponential backoff with max 3 attempts
  4. Persistent: Survives page reloads
// No special handling needed - works automatically
Encore.didGrant('final', transactionId);
// Queued if offline, sent when online

Next Steps