Server-to-server events API for mobile (S2S-mobile)

At a glance: The Server-to-Server (S2S) events for mobile API sends events occurring outside the app to AppsFlyer. S2S events are attributed similarly to SDK events and are available across the platform. 


Server-to-server events API for mobile

The AppsFlyer platform attributes and records events sent by the AppsFlyer SDK and by APIs. Use the S2S API to report events that take place outside the app; for example, a user renews their subscription using your web interface. S2S events, once recorded, are available across the platform, including dashboards, raw data, and analytics. 

AppsFlyer populates S2S events with:

  • Values sent in the S2S message
  • Some AppsFlyer install attribution values like, install time and media source.

API use instructions

Create your API call using the information contained in the sections that follow. 

S2S API facts

API endpoint

  • app_id: The app identifier used in the AppsFlyer dashboard. Insert it precisely as it appears on the dashboard.
  • iOS apps: Ensure to prefix with id.  Failure to do so results in a valid 200 OK return code; without recording the event.
  • Windows: Get the app ID from the Microsoft App Store.
  • Example Android:
HTTP method POST
Accepted content type application/json

--header 'authentication: dev_key'

  • The dev_key is the authentication token in the header. 
  • To get the dev key, in AppsFlyer go to, App Settings > Dev Key
Whitelisting servers to get response messages

Whitelist AWS IP address Ranges to get response messages if your servers are located behind a firewall. 

JSON payload limitation 

JSON payload size: up to1KB

Rate limitation

POST limitation volume: 60,000 POSTs per minute. To increase this limit, contact your CSM.

Coding instructions
Encode URLs

Encode (percent) code reserved characters ( before forming the method URL. 


Use TLS 1.2 or above. Ciphers supported

S2S API facts

Payload parameters

Payload parameters consist of:

  • One or more device identifiers (depending on the operating system).
    • You may be unable to send the identifier for a good reason. For example, because the user has limited ad tracking (LAT). 
    • Failing to send an advertising ID/device identifier can cause attribution failure,  meaning that AppsFlyer can't attribute the event to the media source. 
      • This occurs, for example, if the install record no longer exists due to data retention policies of integrated partners.
      • The device identifier parameter is not mandatory; you can elect not to send any device identifier but take into consideration the attribution consequences of not doing so. 
Operating system Identifier name Description




Where available populate with the device IDFA.

Format: String

Example: "idfa": "9876F1SS-2983-3855-27RR-2R626772VFNB"




Where available populate with the device GAID (advertising ID)

Format: String

Example: "advertising_id": "9c9a82fb-d5de-4cd1-90c3-527441c11828"


Format: String

Example: "oaid": "1fe9a970-efbb-29e0-0bdd-f5dbbf751ab5"


Format: String


Format: String

Example: "imei": "AA-BBBBBB-CCCCCC-D"

Device identifiers
Nom du paramètre Obligatoire Description
appsflyer_id Oui

A unique identifier, generated by AppsFlyer, when the app launches for the first time. 

  • Format: String
  • Exemple : "appsflyer_id": "1415211453000-6513894"
customer_user_id Non

ID utilisateur du client, un identifiant utilisateur unique défini par le propriétaire de l'app.

  • Format: String. 
  • Exemple : "customer_user_id": "my_customer_number1234" 
ip Non

L'adresse IP de l'appareil mobile lorsque l'événement se produit.

  • Si elle est envoyée, l'adresse IP est utilisée pour peupler les champs géographiques.
  • Si elle n'est pas envoyée, AppsFlyer remplit l'adresse IP et les champs géographiques en utilisant les valeurs de l'événement d'attribution d'installation. 
  • Format: String containing IPV4 or IPV6 address
  • Exemple : « ip » : "
af_events_api Oui

Set to true always. 

  • Format: String
  • Exemple : "af_events_api" :"true"
eventName Oui

Specify the event name. Ensure that the event names are aligned with the marketer's requirements.

  • Format: String
  • Exemple : "eventName": "af_purchase"
Valeur de l'événement Oui

Dans le cas où vous envoyez un événement sans valeur, envoyez : "event_value":""

  • Event values need to be sent without additional symbols or formatting. 
  • For af_revenue a decimal comma can be used. Negative values should be preceded by a -
  • Format: Strings in a JSON
  • Exemple : "eventValue": "{ \"af_revenue\": \"6\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\", \"af_quantity\" :\"1\" }"
app_version_name Non

Your app version or identifier.

  • Format: String
  • Example: "app_version_name": "my_app_version"
Heure de l'évènement Non

The time the event occurred using UTC timezone.

  • Default: If noeventTimeis sent, the time is set to the HTTP message arrival time.
  • Format : chaîne aaaa-mm-jj hh:mm:ss.sss l'heure doit être dans le fuseau horaire UTC.
  • Example: "eventTime":"2019-05-15 12:17:01.123" means 12:17:01 UTC. 

Close of day:

  • If you implement eventTime the event must reach AppsFlyer by no later 02:00 of the following day. Meaning, if the event took place on Monday at 17:00 UTC, it must reach AppsFlyer servers by no later than Tuesday 02:00 UTC. 
  • Events received after close of day are stamped with the actual arrival time.
  • Les événements ayant une heure future, c'est-à-dire après l'heure d'arrivée, sont horodatés à l'heure d'arrivée. 


  • Les heures sont en UTC
  • Un événement est envoyé avec eventTime = Lundi 21h00.
Heure d'arrivée Time recorded by AppsFlyer Remarque
Mardi 01h00 Lundi 21h00 Arrived before close of business.
Mercredi 09h00  Mercredi 09h00 Arrived after close of business. Time is set to arrival time. 
eventCurrency Non

Currency code using ISO 4217 3-character codes and BCN (Bitcoin)

Par défaut : USD 

Example: "eventCurrency": "ZAR"

Paramètres d'événement

Curl example

curl --location --request POST '<app_id_placeholder>' \
--header 'authentication: <dev_key_placeholder>
' \
--header 'Content-Type: application/json' \
--data-raw '{
  "appsflyer_id": "9999999999999-9999999999999999999",
	"advertising_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
	"customer_user_id" : "example_customer_id_123",
	"ip": "",
	"af_events_api" :"true",
	"app_version_name" : "example_version_name",
	"eventTime" : "2020-02-25 12:00.000",
	"eventName": "af_purchase",
	"eventCurrency": "ZAR",
		\"af_revenue\": \"1006\",
		\"af_content_type\": \"wallets\",
		\"af_content_id\": \"15854\",
		\"af_quantity\" :\"1\"

Response codes

Response code  Message Gestion
200 OK

On receipt of a message, minimum data validation is performed. As such, you can get an OK response even though the event may not completely record in AppsFlyer. To debug events:

  • Using the CURL example in this article"
    • Update the app id and dev key details.
    • Change the appsflyer_id, and eventTime parameters. Use the current time and an appsflyer_id recently attributed as non-organic.
  • Send the call.
    • It should return with a 200 OK message.
    • Check to see if the vent recorded correctly including the revenue
    • Make additional changes as needed and test again.
400 Échec de l'authentification Ensure that the authentication key is correct.
400  appsflyer_id est un champ obligatoire
  • Make sure you pass the correct AppsFlyer ID in the JSON. 
  • JSON is stringified() and formatted correctly.
401 Unauthorized When the key provided in the authentication header is not the <dev_key> for this app.
400 Bad request

When the request failed at least one of the validation criteria.

400  Charge utile absente ou échec de l'analyse
  • Assurez-vous que le JSON a été converti en chaîne et que son format est correct.
  • If more than one event is included in the JSON payload. Make sure to send one event per request.
500  Erreur interne du serveur  Verify that the JSON is stringified() and formatted correctly.



  • For testing, use the AppsFlyer ID of a non-organic install so that test event attributes in realtime. Organic events attribute with a delay of a few hours.
  • Some event fields are populated using the install attribution event of the user. For example, install time and media source. 

To attribute the test device as a non-organic install:

  1. Prepare a custom attribution link for testing S2S messages. Set the media source parameter on the link as to s2s_test. Example: &pid=s2s_test 
  2. Whitelist the test device
  3. Uninstall the app from the test device.
  4. Send the link to the test device.
  5. Click on the link.
  6. Install and then launch the app.
  7. In the dashboard, check to see that the install is attributed.
  8. Fetch the AppsFlyer ID to use in your S2S message.

To send the S2S test message:

  1. Prepare an S2S message using the AppsFlyer ID allocated. See the following for code examples.
  2. Send the message.
  3. Do one of the following to see how the message is recorded in AppsFlyer:
  4. Check and view that:
    • Values sent in the S2S message correctly populate the report. Pay special attention to the Event date, Event currency, Event revenue, and Event value fields.
    • The attribution source and install time are populated by AppsFlyer.
    • Values provided by the SDK itself like SDK version are not populated.

Example code for sending S2S messages

JavaPythonNode JSC#PHPGo
/* using the okhttp package 
install from maven */
import okhttp3.*;

public class SendRequest {
  public static void main(String[] args) {

    OkHttpClient client = new OkHttpClient();
    MediaType mediaType = MediaType.parse("application/json");

    RequestBody body = RequestBody.create(mediaType, "" +
        "{\r\n\t\"appsflyer_id\": \"<APPS_FLYER_ID>\"," +
        "\r\n\t\"customer_user_id\": \"123456\",\r\n\t\"eventName\": \"af_purchase\",\r\n\t\"" +
        "eventValue\": \"{\\\"af_revenue\\\":\\\"6\\\" ,\\\"af_content_id\\\":\\\"15854\\\"}\",\r\n\t\"" +
        "eventCurrency\": \"USD\",\r\n\t\"" +
        "eventTime\": \"2018-08-10 4:17:00.000\",\r\n\t\"" +
        "af_events_api\" :\"true\"\r\n}");

    Request request = new Request.Builder()
        .addHeader("Content-Type", "application/json")
        .addHeader("authentication", "<YOUR_DEV_KEY>")

    try {
      Response response = client.newCall(request).execute();
    } catch (IOException e) {

Populating parameters

Différence entre organique et non organique

When AppsFlyer processes S2S in-app events, attribution fields are populated by using the AppsFlyer ID to identify the associated install event that preceded the in-app events.

Cela signifie que certaines données qu'AppsFlyer associe à des événements in-app S2S non organiques ne sont pas associées à des événements in-app S2S organiques.


Par exemple, si vous comparez les rapports de données brutes d'événements in-app S2S non organiques et organiques, les événements non organiques contiennent des données que les événements in-app organiques ne contiennent pas.

Non-organic in-app events include data about the media source, campaign, attributed touch type, and attributed touch time.

Organic in-app events, on the other hand, follow an organic install. Organic installs don't have data related to campaign, media source, attributed touch type, and install time.

Mapping AppsFlyer ID with customer user ID (CUID)

Backend logic is required to obtain values to populate parameter. The following describes how you can get the AppsFlyer ID:

  • The AppsFlyer ID is mandatory and is used to attribute events.
  • It is generated when a user first installs the mobile app.
  • So that you can map your CUID to the AppsFlyer ID, you need to set the CUID in the app. 

To make it easier for you to know which user performed which event, implement the flow that follows:

  • Set customer user ID when the user installs the app.
  • AppsFlyer raw data reports contain the CUID and AppsFlyer ID. Use one of the data delivery tools to get this or AppsFlyer Push API. 
  • Use the raw data reports to match the CUID with the AppsFlyer ID. 
  • The AppsFlyer ID is available in the SDK integrated into your app (Android/iOS).
  • Map AppsFlyer ID to customer user ID in your internal systems (important for future use).

Once you map AppsFlyer ID with your CUID, you can match the user to events performed. You can then obtain the other values (event value, event currency, event time, etc.) and send the server to server in-app event. 

 Request body example

  "appsflyer_id": "1415211453000-6513894",
	"advertising_id": "38412345-8cf0-aa78-b23e-10b96e40000d",
	"eventName": "af_purchase",
		\"af_revenue\": \"6\",
		\"af_content_type\": \"wallets\",
		\"af_content_id\": \"15854\",
		\"af_quantity\" :\"1\"
	"eventCurrency": "USD",
	"ip": "",
	"eventTime": "2014-05-15 12:17:00.000",
	"af_events_api" :"true"

In this case, AppsFlyer receives an S2S in-app event which represents a purchase event with revenue, as well as additional properties such as content type, etc.

Fetching the AppsFlyer ID

appsflyer_id is a mandatory parameter in server-to-server event messages. AppsFlyer uses this parameter to attribute the event to the original device and attributed media source. You can get the ID using one of the following methods:


Pendant le test des messages S2S, si vous utilisez des Raw Data ou des API Push ou Pull, recherchez l'enregistrement doté de la source média « s2s_test ». Il s’agit de votre appareil de test et son ID d'appareil AppsFlyer est l’ID dont vous avez besoin.

Envoi de revenus négatifs

Events having a negative revenue value can be sent. For example, if a purchase was canceled. The parameter af_revenue can have negative values to record this. 

If you populate af_quantity, you might want to populate it with a negative value depending on your system logic. AppsFlyer doesn't make use of af_quantity.

Example with negative revenue

	"eventName": "cancel_purchase",
		\"af_revenue\": \"-6\",
		\"af_content_type\": \"wallets\",
		\"af_content_id\": \"15854\",
		\"af_quantity\" :\"1\"
	"eventCurrency": "USD",

Sending events without event values

Si vous souhaitez envoyer des événements sans valeur d'événement, transmettez simplement une chaîne vide à la valeur d'événement : "event_value": ""

AppsFlyer est alors en mesure, selon la configuration de l’annonceur, d’envoyer de tels événements rich in-app aux sources médias aux fins de ciblage avancé, d'optimisation et de création de public.


Events aren't displaying in the dashboard

  • Endpoint: Verify that the endpoint used is correct.
  • Verify that the payload contains the mandatory parameters. See here.
  • Ensure that the AppsFlyer ID you are using to fire the event is a real appsflyer_id and is actually installed on the specific app. Not a test ID provided in the documentation. See here.
  • S2S events don't support multi-events in one S2S request. Each event must be sent as a separate event. 
  • In the overview dashboard, the date range relates to the app install date (LTV) and not to the event date.
    • Make sure you select the correct date range.
    • Make sure that the dashboard date range corresponds to the install date of the device (appsflyer_id) and not the date of the event.
  • Event raw data reports: the date range relates to the event date and not the install date. 

Events don't contain revenue

If you send S2S events but their revenue is not recorded: ensure that the JSON that you send is stringified. The most important part is the event value parameter in the JSON. It must be stringified as shown in the example that follows.

"{\"af_revenue\":\"6\" ,\"af_content_type\":\"wallets\"}"

If it is not stringified then the event value is not processed correctly and revenue is not recorded.

Revenue values must not be formatted in any way. They can contain a decimal comma but this is not obligatory. Don't include currency signs or codes nor , delimiters. The revenue can be prefixed by a -

  • Example valid values are: 123, -123.45123.456 
  • Example Illegal values: 1,234.561,234

Not all fields are populated in S2S events

Raw data fields are populated using the value sent in the S2S call and some fields are populated using the install event.  Similar behavior but not identical is observed for in-app events reported using the AppsFlyer SDK. There are some differences, specifically, the following fields are not populated for S2S events:

  • WIFI
  • Operator
  • Language
  • Device Type
  • Device Category
  • App Version: Set using app_version_name.
  • App Name
Cet article vous a-t-il été utile ?