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
Grant type: 'provisional' or 'final'
'provisional': User completed offer action (sent automatically by SDK)
'final': Publisher confirms delivery of entitlement (e.g., subscription activated)
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
The transaction ID to check for verification
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
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
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:
- Queued: Stored in
localStorage when offline
- Sent: Automatically sent when connectivity restored
- Retried: Exponential backoff with max 3 attempts
- Persistent: Survives page reloads
// No special handling needed - works automatically
Encore.didGrant('final', transactionId);
// Queued if offline, sent when online
Next Steps