TikTok for Developers

Docs

Direct Message Sharing

Enable users to share content directly to their friends via TikTok's direct messaging system. This guide covers the implementation of friend list sharing, open data domain communication, and social sharing capabilities within mini games.

The direct message sharing capability allows mini games to:

  • Display a friend list interface for users to select recipients
  • Share game content directly to specific friends via TikTok messages
  • Store and retrieve user data in the cloud for leaderboards and game state
  • Communicate between the main game domain and the open data domain

Prerequisites

Before implementing direct message sharing, ensure you have the following:

  • A mini game registered on the Developer Portal
  • Client version 40.3.0 or higher for sharing capabilities
  • Understanding of the open data domain architecture

Version compatibility check

Use the canIUse API to verify that the user's client supports the sharing functionality:

// Check if shareAppMessage is supported
const isSupported = TTMinis.game.canIUse('shareAppMessage');
if (!isSupported) {
  // Fallback behavior or prompt user to update
  console.log('Please update TikTok to the latest version to use this feature.');
}

Architecture overview

The direct message sharing system uses a dual-domain architecture:

  1. Main Game Domain: The primary game environment where your game logic runs
  2. Open Data Domain: An isolated environment for handling sensitive social data (friend lists, user information)

Communication between these domains happens through a secure message-passing system using a shared canvas.

Set up the open data domain

The open data domain is required to access social features like friend lists and direct messaging. This domain runs in an isolated iframe with limited capabilities for security purposes.

Project configuration

Add the open data context configuration to your project.config.json file:

{
  "dev": {
    "port": 9527
  },
  "openDataContext": "openDataContext",
  "app_id": "your_client_key"
}

The openDataContext field specifies the directory containing your open data domain code. The system will automatically load openDataContext/index.js as the entry point.

Native game engine integration

For games built with Cocos, Laya, Egret, or other native game engines, the open data domain can be configured to work alongside your main game. Refer to your game engine's documentation for specific integration steps.

Canvas configuration

The open data domain renders content on a shared canvas that overlays your main game. You must configure this canvas position and dimensions appropriately.

Set canvas position and dimensions

// Get the shared canvas from the game global context
let sharedCanvas = GameGlobal.tt.getSharedCanvas();
let sharedContext = sharedCanvas.getContext('2d');

// Set the canvas resolution (internal coordinate system)
sharedCanvas.width = 800;
sharedCanvas.height = 800;

// Set the display size and position using CSS-style properties
sharedCanvas.style.width = '80vw';      // 80% of viewport width
sharedCanvas.style.height = '40vh';     // 40% of viewport height
sharedCanvas.style.top = '20vh';        // Position from top
sharedCanvas.style.left = '10vw';       // Position from left

Canvas best practices

  • Set the internal resolution (width/height) high enough for crisp rendering on high-DPI displays
  • Use viewport-relative units (vw, vh) for responsive positioning across different screen sizes
  • Ensure the canvas doesn't obscure critical game UI elements

Mini Game Domain APIs

These methods are called from your main game code to interact with the open data domain and social features.

getOpenDataContext

Retrieves a reference to the open data context for message passing.

const openDataContext = TTMinis.game.getOpenDataContext();
const { postMessage } = openDataContext;

// Send a message to the open data domain
postMessage({
  type: 'show',
  data: {}
});

For native games, use the global tt object:

const { postMessage } = tt.getOpenDataContext();
postMessage({ type: 'show', data: {} });

authorizeOpenContext

Requests authorization to access the user's friend list and social data. This must be called before accessing any social features.

TTMinis.game.authorizeOpenContext({
  // When get_status_only is true, only checks status without showing UI
  get_status_only: false,
  success: (result) => {
    const { code } = result;
    // Use the code for authentication
    console.log('Authorization code:', code);
  },
  fail: (error) => {
    console.error('Authorization failed:', error);
  },
  complete: () => {
    console.log('Authorization flow completed');
  }
});

Native implementation:

tt.authorizeOpenContext({
  get_status_only: false,
  success: (result) => {
    const { code } = result;
  },
  fail: () => {},
  complete: () => {}
});

Parameters

Field

Type

Description

Required

get_status_only

boolean

When true, only checks authorization status without displaying the authorization dialog

No

success

function

Callback on successful authorization

No

fail

function

Callback on authorization failure

No

complete

function

Callback when authorization flow completes (success or failure)

No

Success Callback Result

Field

Type

Description

code

string

Authorization code for authenticating with the open data domain

setUserCloudStorage

Stores user data in the cloud for persistence across sessions and for sharing with friends (e.g., high scores, game progress).

TTMinis.game.setUserCloudStorage({
  data: [
    { key: 'highScore', value: '10000' },
    { key: 'level', value: '15' }
  ],
  success: () => {
    console.log('Data saved successfully');
  },
  fail: (error) => {
    console.error('Failed to save data:', error);
  }
});

Native implementation:

tt.setUserCloudStorage({
  data: [{ key: 'highScore', value: '10000' }],
  success: () => {},
  fail: () => {}
});

Parameters

Field

Type

Description

Required

data

Array<Object>

Array of key-value pairs to store

Yes

data[].key

string

Key name (max 128 characters)

Yes

data[].value

string

Value to store (max 2048 characters)

Yes

success

function

Callback on successful save

No

fail

function

Callback on save failure

No

complete

function

Callback when operation completes

No

removeUserCloudStorage

Removes specific keys from the user's cloud storage.

TTMinis.game.removeUserCloudStorage({
  keyList: ['highScore', 'tempData'],
  success: () => {
    console.log('Data removed successfully');
  },
  fail: (error) => {
    console.error('Failed to remove data:', error);
  }
});

Native implementation:

tt.removeUserCloudStorage({
  keyList: ['highScore'],
  success: () => {},
  fail: () => {}
});

Parameters

Field

Type

Description

Required

keyList

Array<string>

Array of keys to remove

Yes

success

function

Callback on successful removal

No

fail

function

Callback on removal failure

No

complete

function

Callback when operation completes

No

Open Data Domain APIs

These methods are called from within the open data domain (openDataContext/index.js). The open data domain has limited access to system APIs and primarily handles rendering the friend list and processing social data.

onMessage

Listens for messages sent from the main game domain via postMessage.

tt.onMessage((data) => {
  console.log('Received message from main domain:', data);

  switch (data.command) {
    case 'render':
      // Render the friend list
      renderFriendList();
      break;
    case 'hide':
      // Clear the canvas
      clearCanvas();
      break;
    default:
      console.log('Unknown command:', data.command);
  }
});

Animation Frame Methods

The open data domain supports standard animation frame methods for smooth rendering:

// Request an animation frame
const frameId = tt.requestAnimationFrame(() => {
  // Render logic here
  drawFriendList();
});

// Cancel a scheduled animation frame
tt.cancelAnimationFrame(frameId);

Timer Methods

Standard timer methods are available in the open data domain:

// Set a timeout
const timeoutId = tt.setTimeout(() => {
  console.log('Timeout executed');
}, 1000);

// Clear a timeout
tt.clearTimeout(timeoutId);

getSharedCanvas

Gets the shared canvas for rendering the open data domain content.

const canvas = tt.getSharedCanvas();
const ctx = canvas.getContext('2d');

// Set canvas dimensions
canvas.width = 400;
canvas.height = 200;

// Draw content
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);

createImage

Creates an image object for loading and displaying images in the open data domain.

const image = tt.createImage();
image.src = 'https://example.com/image.png';
image.onload = () => {
  console.log('Image loaded successfully');
  // Draw image to canvas
  ctx.drawImage(image, 0, 0);
};
image.onerror = (error) => {
  console.error('Failed to load image:', error);
};

getFriendCloudStorage

Retrieves cloud storage data for the user's friends. This is useful for building leaderboards.

GameGlobal.tt.getFriendCloudStorage({
  keyList: ['highScore', 'level'],
  success: (data) => {
    console.log('Friend data retrieved:', data);
    // Process friend data for leaderboard display
    data.data.forEach(friend => {
      console.log(`Friend: ${friend.nickname}, Score: ${friend.KVDataList[0].value}`);
    });
  },
  fail: (error) => {
    console.error('Failed to get friend data:', error);
  },
  complete: () => {
    console.log('Friend data request completed');
  }
});

Parameters

Field

Type

Description

Required

keyList

Array<string>

Array of keys to retrieve for each friend

Yes

success

function

Callback on successful retrieval

No

fail

function

Callback on retrieval failure

No

complete

function

Callback when operation completes

No

Success Callback Result

Field

Type

Description

data

Array<Object>

Array of friend data objects

data[].avatarUrl

string

Friend's avatar URL

data[].nickname

string

Friend's nickname

data[].openid

string

Friend's OpenID (unique identifier)

data[].KVDataList

Array<Object>

Array of key-value pairs for the requested keys

getUserCloudStorage

Retrieves the current user's cloud storage data within the open data domain.

GameGlobal.tt.getUserCloudStorage({
  keyList: ['key_test_abc'],
  success: (data) => {
    console.log('User data retrieved:', data);
  },
  fail: (error) => {
    console.error('Failed to get user data:', error);
  },
  complete: () => {
    console.log('User data request completed');
  }
});

setUserCloudStorage (Open Data Domain)

Sets cloud storage data from within the open data domain.

GameGlobal.tt.setUserCloudStorage({
  data: [
    { key: 'key_test_abc', value: 'value_test_abc' }
  ],
  success: (data) => {
    console.log('Data saved:', data);
  },
  fail: (error) => {
    console.error('Failed to save:', error);
  },
  complete: () => {
    console.log('Save operation completed');
  }
});

removeUserCloudStorage (Open Data Domain)

Removes cloud storage data from within the open data domain.

GameGlobal.tt.removeUserCloudStorage({
  keyList: ['key_test_abc'],
  success: (data) => {
    console.log('Data removed:', data);
  },
  fail: (error) => {
    console.error('Failed to remove:', error);
  },
  complete: () => {
    console.log('Remove operation completed');
  }
});

Social sharing methods

shareToUser

Initiates sharing to a specific friend via direct message. This method can only be called from within the open data domain.

tt.shareToUser({
  imageUrl: 'https://example.com/share-image.png',
  openid: 'friend_openid_from_friend_list',
  subtitle: 'Check out my new high score!',
  templateType: 1,
  title: 'Beat my score in Super Game!',
  success: () => {
    console.log('Share completed successfully');
  },
  fail: (error) => {
    console.error('Share failed:', error);
  }
});

Parameters

Field

Type

Description

Required

imageUrl

string

URL of the image to display in the share card

Yes

openid

string

OpenID of the friend to share with (obtained from friend list)

Yes

subtitle

string

Subtitle text displayed in the share card

No

templateType

number

Visual template style: 1 or 2

No

title

string

Main title displayed in the share card

No

success

function

Callback on successful share

No

fail

function

Callback on share failure

No

Template types

Two visual templates are available for share cards:

  • Template Type 1: CTA template, standard share card layout with title, subtitle, and image
  • Template Type 2: Interactive template, alternative layout with different visual styling

Choose the template that best fits your game's branding and content.

shareAppMessage

Initiates sharing via direct message from the main game domain, opening the contact selection interface. This method can only be called from the main game domain.

Note: Requires client version 40.3.0 or higher.

tt.shareAppMessage({
  imageUrl: 'https://example.com/share-image.png',
  subtitle: 'Join me in this amazing game!',
  templateType: 1,
  title: 'Play Super Game with me!',
  success: () => {
    console.log('Share initiated successfully');
  },
  fail: (error) => {
    console.error('Share failed:', error);
  }
});

Parameters

Field

Type

Description

Required

imageUrl

string

URL of the image to display in the share card

Yes

subtitle

string

Subtitle text displayed in the share card

No

templateType

number

Visual template style: 1 or 2

No

title

string

Main title displayed in the share card

No

success

function

Callback on successful share

No

fail

function

Callback on share failure

No

shareToStory

Allows users to share content to their TikTok story. This method can only be called from the main game domain.

tt.shareToStory({
  success: () => {
    console.log('Shared to story successfully');
  },
  fail: (error) => {
    console.error('Share to story failed:', error);
  }
});

Implementation example

Here's a complete example showing how to implement the friend list sharing flow:

Main Game Domain

class Game {
  private openDataContext: any;

  constructor() {
    // Initialize open data context
    this.openDataContext = TTMinis.game.getOpenDataContext();

    // Check if sharing is supported
    if (!TTMinis.game.canIUse('shareAppMessage')) {
      console.warn('direct message sharing not supported on this client version');
      return;
    }
  }

  // Show friend list for sharing
  showFriendList() {
    // Request authorization first
    TTMinis.game.authorizeOpenContext({
      get_status_only: false,
      success: (result) => {
        // Send message to open data domain to render friend list
        this.openDataContext.postMessage({
          type: 'showFriendList',
          data: {
            authCode: result.code
          }
        });
      },
      fail: (error) => {
        console.error('Authorization failed:', error);
      }
    });
  }

  // Hide friend list
  hideFriendList() {
    this.openDataContext.postMessage({
      type: 'hide'
    });
  }

  // Save high score
  saveHighScore(score: number) {
    TTMinis.game.setUserCloudStorage({
      data: [
        { key: 'highScore', value: score.toString() }
      ],
      success: () => {
        console.log('High score saved');
      }
    });
  }

  // Share via direct message (main domain method)
  shareToFriend() {
    tt.shareAppMessage({
      title: 'Beat my high score!',
      subtitle: `I just scored ${this.currentScore} points!`,
      imageUrl: 'https://your-cdn.com/share-card.png',
      templateType: 1,
      success: () => {
        console.log('Share successful');
      }
    });
  }
}

Open Data Domain (openDataContext/index.js)

// Listen for messages from main domain
tt.onMessage((message) => {
  switch (message.type) {
    case 'showFriendList':
      renderFriendList();
      break;
    case 'hide':
      clearCanvas();
      break;
  }
});

// Get canvas context
const canvas = tt.getSharedCanvas();
const ctx = canvas.getContext('2d');

// Render friend list
function renderFriendList() {
  // Get friend data with cloud storage
  tt.getFriendCloudStorage({
    keyList: ['highScore'],
    success: (result) => {
      const friends = result.data;

      // Clear canvas
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      // Draw friend list
      friends.forEach((friend, index) => {
        const y = index * 80 + 20;

        // Draw avatar
        const avatar = tt.createImage();
        avatar.src = friend.avatarUrl;
        avatar.onload = () => {
          ctx.drawImage(avatar, 20, y, 60, 60);
        };

        // Draw name
        ctx.fillStyle = '#333333';
        ctx.font = '16px sans-serif';
        ctx.fillText(friend.nickname, 100, y + 30);

        // Draw high score
        const score = friend.KVDataList[0]?.value || '0';
        ctx.fillStyle = '#666666';
        ctx.font = '14px sans-serif';
        ctx.fillText(`Score: ${score}`, 100, y + 50);
      });
    }
  });
}

// Clear canvas
function clearCanvas() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
}

// Handle share to specific user
function shareToFriend(openid) {
  tt.shareToUser({
    openid: openid,
    title: 'Join me in this game!',
    subtitle: 'Let\'s play together',
    imageUrl: 'https://your-cdn.com/share-card.png',
    templateType: 1,
    success: () => {
      console.log('Shared successfully');
    }
  });
}

Best Practices

  1. Version Checking: Always use canIUse to check if sharing features are available before attempting to use them.
  2. Error Handling: Implement proper error handling for all sharing methods to gracefully handle network issues or user cancellations.
  3. Performance: The open data domain runs in a separate context. Minimize message passing between domains and batch operations when possible.
  4. User Experience: Show loading states while fetching friend data and provide clear feedback when sharing succeeds or fails.
  5. Data Privacy: Only request the minimum necessary permissions. Use get_status_only: true to check authorization status without prompting the user when appropriate.
  6. Canvas Management: Ensure the shared canvas is properly sized and positioned to not interfere with game controls.

Troubleshooting

Issue

Possible Cause

Solution

canIUse('shareAppMessage') returns false

Client version too old

Prompt user to update TikTok to version 40.3.0 or higher

Friend list not displaying

Open data domain not configured

Check project.config.json has correct openDataContext path

Canvas content not visible

Canvas positioning incorrect

Verify canvas style properties (width, height, top, left) are set correctly

Authorization fails

User declined permission

Handle fail callback and provide alternative flow or explanation

Cloud storage data not persisting

Key format incorrect

Ensure keys are strings and values are JSON-serializable

Share card not displaying correctly

Image URL inaccessible

Verify image URL is publicly accessible and properly formatted

Was this document helpful?
TikTok for Developers