Custom activities#

A custom activity records an action in your referral program that Talkable can’t see on its own. For example, a person opens a screen in your app, taps a button, or finishes a step on your backend. You send each activity with one API call. Talkable stores it with the person, the time, and the offer, if you link one. The Custom Activities report lists the activities, so you can compare how many people reach each one. See Track a custom activity in the API reference.

How custom activities work#

Each custom activity has a name, called a key. An activity sent through the API belongs to one person. When you send the Advocate’s offer code with the activity, Talkable links it to that offer and copies its campaign and traffic source. An activity can also happen before the person has an offer, for example on the screen that shows your referral entry point.

You can send activities from three places:

  • Your backend — with the API key, for actions on your server, such as a completed profile.

  • Your mobile app — with the Public API key, for actions in the app.

  • A Talkable campaign page — with the page script, for actions on the Advocate share page or the Friend Claim Page.

For example, an app can track the steps of its referral screen:

Key

When the app sends it

profile_page_viewed

The screen with the referral entry point is shown

referral_entry_clicked

The user taps the referral entry point

referral_page_viewed

The referral screen is shown

share_action_taken

The user copies the link or picks an app to share

invite_contacts_clicked

The user opens the contacts list

contacts_invited

The user invites contacts, with their number in quantity

Name your activities#

A key has 3 to 50 characters: lowercase letters and numbers, with single underscores between words. It starts with a letter and doesn’t end with an underscore. A site can have up to 50 keys.

A call with the private API key registers a new key on first use. Keys stay registered. Choose them before your release, and send them exactly as written. Put extra detail, such as the platform or app version, in properties.

Send an activity#

Send each activity to the person’s endpoint, with the site in the query string:

curl -X POST \
  'https://www.talkable.com/api/v2/people/jane%40example.com/custom_activities?site_slug=example-site' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "data": {
      "key": "contacts_invited",
      "idempotency_key": "c2e8d1f4-9a3b-4e6c-8d7f-1b5a9c3e2f60",
      "offer_short_url_code": "a1B2c3",
      "quantity": 3,
      "properties": {"platform": "ios", "app_version": "5.2.0"}
    }
  }'

Identify the person who took the action by their email, URL-encoded in the path. For an Advocate, use the email from their offer. A new email creates the person, unless your site identifies people by phone number. With the private API key, you can also use a customer ID, phone number, or username.

Field

Use

key

Required. The activity’s key

idempotency_key

Required. Your ID for this activity, 1 to 255 printable ASCII characters, for example a UUID

offer_short_url_code

The Advocate’s offer. Links the activity to the offer and its campaign

visitor_offer_id

The Friend’s offer, for an activity on the Friend’s side. The default destination URL carries it as talkable_visitor_offer_id

campaign_id

The campaign, for an activity before the person has an offer

role

advocate or friend, for an activity without an offer. With an offer, Talkable sets it

quantity

A whole number from 1 to 10,000. The default is 1

sharing_channel

For share actions: other for a copied link, or a channel such as sms or whatsapp

occurred_at

When the action happened, in ISO 8601 with Z or an offset. From 7 days back to 5 minutes ahead. The default is the time of the call

properties

A flat object of strings, numbers, booleans, or nulls, up to 2 KB as JSON. No personal data

Send at most one of offer_short_url_code, visitor_offer_id, and campaign_id. Any field not listed in the API reference is refused.

A recorded activity returns 201 with the stored activity:

{
  "ok": true,
  "result": {
    "custom_activity": {
      "id": 97,
      "key": "contacts_invited",
      "quantity": 3,
      "role": "advocate",
      "offer_short_url_code": "a1B2c3",
      "visitor_offer_id": null,
      "campaign_id": 3703,
      "sharing_channel": null,
      "properties": {"platform": "ios", "app_version": "5.2.0"},
      "created_at": "2026-10-30T05:26:59.000-07:00"
    }
  }
}

Retry safely#

Create a new idempotency_key for each action, including repeats of the same action. To retry a call that failed or timed out, send it again with the same API key, idempotency_key, and data. Talkable answers 201 with the stored activity and records it once. The same idempotency_key with different data returns 400.

Activities sent later, for example after the app was offline, carry the time of the action in occurred_at.

From a mobile app#

An app can send activities directly with the Public API key. These calls follow a few extra rules:

  • Only keys that are already registered are accepted. Register a new key once with the private API key, from your backend.

  • Identify the person by email, customer ID, or phone number.

  • Use a random idempotency_key, such as a UUID.

  • A site accepts up to 20,000 of these calls a day from one IP address, retries included. Further calls return 429.

The report shows these activities with the source Mobile app. Activities sent with the private API key show the source API.

From a campaign page#

A Talkable campaign page, such as the Advocate share page or the Friend Claim Page, can record an activity with the page script:

Talkable.trackCustomActivity('share_action_taken', {
  sharing_channel: 'other'
}, onSuccess, onError);

The page script takes quantity, sharing_channel, properties, and idempotency_key. It accepts only registered keys. It uses the offer on the page, and the person if the page knows them. The report shows these activities with the source Page script.

Review activities#

The Custom Activities report lists every recorded activity, in All reports under Support. Filter by period, key, person, role, source, or campaign, and export the rows as CSV. The optional QA column marks QA activities. Users with the Read role and above can open the report.

Errors#

An API call with invalid data returns 400 with {"ok": false, "error_message": "..."}. The message names the problem:

Message includes

What to change

Can't accept parameter

Remove a field the endpoint doesn’t take

Unknown custom activity

Register the key with the private API key first

Validation failed: Identifier

Fix the key’s format

Duplicate entity

Use a new idempotency_key for a new action

must be within the last 7 days

Send a time from the last 7 days and at most 5 minutes ahead, or leave it out

No offer found by

Check the offer code and the site

Validation failed: Person does not match

Send the email of the offer’s owner

A read-only API key returns 401. A public-key call over the daily limit returns 429.

Next steps#