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 Descripción




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
Nombre de parámetro Obligatorio Descripción

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

  • Format: String
  • Ejemplo: "appsflyer_id": "1415211453000-6513894"
customer_user_id No

ID de usuario de cliente, un identificador de usuario único establecido por el propietario de la aplicación.

  • Format: String. 
  • Ejemplo: "customer_user_id": "my_customer_number1234" 
ip No

La dirección IP del dispositivo móvil durante la ocurrencia del evento.

  • Si se envía, la dirección IP se usa para completar los campos de geolocalización.
  • Si no se envía, AppsFlyer completa la dirección IP y los campos de geolocalización utilizando los valores del evento de atribución de la instalación. 
  • Format: String containing IPV4 or IPV6 address
  • Ejemplo: "ip": "

Set to true always. 

  • Format: String
  • Ejemplo: "af_events_api" :"true"

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

  • Format: String
  • Ejemplo: "eventName": "af_purchase"
valor del evento

Si envías un evento sin ningún valor, entonces envía: "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
  • Ejemplo: "eventValue": "{1}"
app_version_name No

Your app version or identifier.

  • Format: String
  • Example: "app_version_name": "my_app_version"
Hora del evento No

The time the event occurred using UTC timezone.

  • Default: If noeventTimeis sent, the time is set to the HTTP message arrival time.
  • Formato: string yyyy-mm-dd hh:mm:ss.sss, la hora debe estar en la zona horaria 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.
  • Los eventos que tienen una hora futura, es decir, después de la hora de llegada, se marcan con la hora de llegada. 


  • Las horas están en UTC
  • Se envía un evento con eventTime = Monday 21:00.
Hora de llegada Time recorded by AppsFlyer Observación
Martes 01:00 Lunes 21:00 Arrived before close of business.
Miércoles 09:00  Miércoles 09:00 Arrived after close of business. Time is set to arrival time. 
eventCurrency No

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

Predeterminado: USD 

Example: "eventCurrency": "ZAR"

Parámetros del evento

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 Cómo manejarlo
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 Error de autenticación Ensure that the authentication key is correct.
400  appsflyer_id es un campo obligatorio
  • 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  Falta la carga de datos o no se la pudo analizar
  • Asegúrese de que el JSON se haya pasado a secuencia y que se le haya dado el formato correcto.
  • If more than one event is included in the JSON payload. Make sure to send one event per request.
500  Error interno del servidor  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

Diferencia entre orgánicos y no orgánicos

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.

Esto significa que algunos datos que AppsFlyer asocia con eventos in-app S2S no orgánicos no se asocian con eventos in-app S2S orgánicos.


Por ejemplo, si se comparan los reportes de datos sin procesar de eventos in-app S2S orgánicos y no orgánicos, se observa que los eventos no orgánicos contienen datos que no los orgánicos no.

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:


Cuando pruebes mensajes S2S, si estás usando raw data o las API Push o Pull, busca el registro con la fuente de medios "s2s_test". Este es tu dispositivo de prueba y su ID de dispositivo de AppsFlyer es el ID que necesitas.

Envío de ingresos negativos

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 quiere enviar eventos sin valor de evento, simplemente envíe una secuencia vacía al valor del evento: "event_value":""

De esta manera, AppsFlyer puede, según la configuración del anunciante, enviar tales eventos in-app enriquecidos a fuentes de medios con fines avanzados de targeting, optimización y creación de audiencias.

Resolución de Problemas

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
¿Fue útil este artículo?