This article helps you:
Understand how JSON payloads work in Amplitude Experiment
Create and configure variants with JSON payloads
Access variant payloads in your application code
JSON payloads let you attach dynamic configuration data to experiment variants. This enables you to remotely change your application's behavior or appearance without redeploying code. Instead of hard-coding different experiences for each variant, you can define the differences in a JSON payload and let your application adapt based on the payload it receives.
When you create a variant, you can optionally attach a JSON payload containing any variables you want to pass to your application. When your application evaluates an experiment, the SDK returns the assigned variant along with its payload. Your application then uses this payload to configure the experience dynamically.
You can add JSON payloads to variants through the Amplitude UI or programmatically through the Management API.
To add a payload when creating a variant:
This payload configures a blog layout feature:
{
"layout": "cards",
"titlePosition": "above",
"gradient": false,
"showDescription": true,
"cardCount": 3
}
For more details about creating variants in the UI, go to Create variations.
You can create variants with payloads using the Experiment Management API. Include the payload field in your request body when creating a variant.
curl --request POST \
--url 'https://experiment.amplitude.com/api/1/flags/{id}/variants' \
--header 'Authorization: Bearer <management-api-key>' \
--header 'Content-Type: application/json' \
--data '{
"key": "cards-layout",
"name": "Cards Layout",
"description": "Blog posts displayed in card format",
"payload": {
"layout": "cards",
"titlePosition": "above",
"gradient": false,
"showDescription": true,
"cardCount": 3
},
"rolloutWeight": 1
}'
For complete API documentation, go to:
After you create variants with payloads, your application needs to evaluate the experiment and access the payload. The implementation varies by SDK, but the general pattern is the same:
import { Experiment } from '@amplitude/experiment-js-client';
// Initialize the Experiment client
const experiment = Experiment.initialize('<DEPLOYMENT_KEY>', {
// Optional configuration
});
// Fetch variants for the current user
await experiment.fetch({
user_id: 'user123',
device_id: 'device456'
});
// Get the variant for a specific flag
const variant = experiment.variant('blog-layout-flag');
// Access the payload
if (variant && variant.payload) {
const layout = variant.payload.layout || 'list';
const titlePosition = variant.payload.titlePosition || 'below';
const showDescription = variant.payload.showDescription !== false;
const cardCount = variant.payload.cardCount || 5;
// Use the payload to configure your UI
configureBlogLayout({
layout,
titlePosition,
showDescription,
cardCount
});
}
import { useEffect, useState } from 'react';
import { Experiment } from '@amplitude/experiment-js-client';
function BlogComponent() {
const [layoutConfig, setLayoutConfig] = useState({
layout: 'list', // Default values
titlePosition: 'below',
showDescription: true,
cardCount: 5
});
useEffect(() => {
async function fetchExperiment() {
const experiment = Experiment.initialize('<DEPLOYMENT_KEY>');
await experiment.fetch({
user_id: 'user123'
});
const variant = experiment.variant('blog-layout-flag');
if (variant && variant.payload) {
setLayoutConfig({
layout: variant.payload.layout || 'list',
titlePosition: variant.payload.titlePosition || 'below',
showDescription: variant.payload.showDescription !== false,
cardCount: variant.payload.cardCount || 5
});
}
}
fetchExperiment();
}, []);
return (
<BlogLayout
layout={layoutConfig.layout}
titlePosition={layoutConfig.titlePosition}
showDescription={layoutConfig.showDescription}
cardCount={layoutConfig.cardCount}
/>
);
}
import { Experiment } from '@amplitude/experiment-node-server';
const experiment = Experiment.initialize('<DEPLOYMENT_KEY>');
async function getExperimentConfig(userId) {
// Fetch variants for the user
const user = {
user_id: userId
};
const variants = await experiment.fetch(user);
const variant = variants['blog-layout-flag'];
// Return the payload or defaults
if (variant && variant.payload) {
return {
layout: variant.payload.layout || 'list',
titlePosition: variant.payload.titlePosition || 'below',
showDescription: variant.payload.showDescription !== false,
cardCount: variant.payload.cardCount || 5
};
}
// Return default configuration if no variant
return {
layout: 'list',
titlePosition: 'below',
showDescription: true,
cardCount: 5
};
}
// Use in your server route
app.get('/api/blog-config', async (req, res) => {
const config = await getExperimentConfig(req.user.id);
res.json(config);
});
Follow these best practices to ensure your payload implementation is robust and maintainable.
Always provide default values when accessing payload properties. This ensures your application works correctly if:
const layout = variant?.payload?.layout || 'list';
Consider validating the payload structure to catch configuration errors early:
function validateLayoutPayload(payload) {
const validLayouts = ['list', 'cards', 'grid'];
if (!validLayouts.includes(payload.layout)) {
console.error('Invalid layout in payload:', payload.layout);
return false;
}
if (typeof payload.cardCount !== 'number' || payload.cardCount < 1) {
console.error('Invalid cardCount in payload:', payload.cardCount);
return false;
}
return true;
}
const variant = experiment.variant('blog-layout-flag');
if (variant?.payload && validateLayoutPayload(variant.payload)) {
applyLayoutConfig(variant.payload);
}
Keep payload structures simple and focused on configuration. Avoid:
Document the expected structure of your payloads, especially when multiple teams work on the same experiments:
/**
* Blog Layout Flag Payload Schema
* @typedef {Object} BlogLayoutPayload
* @property {('list'|'cards'|'grid')} layout - The layout style
* @property {('above'|'below')} titlePosition - Title position relative to content
* @property {boolean} gradient - Whether to show gradient backgrounds
* @property {boolean} showDescription - Whether to show post descriptions
* @property {number} cardCount - Number of cards to display (1-10)
*/
JSON payloads support a wide range of use cases. Here are some of the most common patterns.
Use payloads to remotely configure features without deploying code:
{
"apiEndpoint": "https://api.v2.example.com",
"timeout": 5000,
"retries": 3,
"enableCache": true
}
Configure UI elements like colors, layouts, and text:
{
"primaryColor": "#007AFF",
"buttonText": "Get Started",
"showBanner": true,
"bannerMessage": "Limited time offer!"
}
Gradually enable features with increasing levels of functionality:
{
"enableAdvancedSearch": true,
"enableFilters": true,
"maxResults": 50,
"showRecommendations": true
}
Test different content approaches:
{
"headline": "Transform Your Workflow",
"subheadline": "Get started in minutes, not hours",
"ctaText": "Start Free Trial",
"showTestimonials": true
}
When you access a variant from the SDK or Evaluation API, you can use only the value and payload properties. To access other variant properties like name and description, use the Management API or the Amplitude UI.
For more information about the variant data model, go to Variants.
November 19th, 2024
Need help? Contact Support
Visit Amplitude.com
Have a look at the Amplitude Blog
Learn more at Amplitude Academy
© 2025 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.