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 Descrição




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
Nome do parâmetro Obrigatório Descrição
appsflyer_id Sim

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

  • Format: String
  • Exemplo: "appsflyer_id ": " 1415211453000-6513894"
customer_user_id Não

ID do usuário cliente, um identificador de usuário configurado pelo proprietário do aplicativo.

  • Format: String. 
  • Exemplo: "customer_user_id ": "my_customer_number1234"
ip Não

O endereço de IP do dispositivo móvel durante a ocorrência do evento.

  • Se enviado, o endereço de IP é usado para preencher campos de geolocalização.
  • Se não for enviado, a AppsFlyer preenche o endereço de IP e os campos de geolocalização usando os valores do evento de atribuição de instalação. 
  • Format: String containing IPV4 or IPV6 address
  • Exemplo: "ip": "
af_events_api Sim

Set to true always. 

  • Format: String
  • Exemplo: " af_events_api": " true "
eventName Sim

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

  • Format: String
  • Exemplo: "eventName": "af_purchase"
Valor do evento Sim

Se você enviar um evento sem um valor, envie: " 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
  • Exemplo: "eventValue": "{ \"af_revenue\": \"6\", \"af_content_type\": \"wallets\", \"af_content_id\": \"15854\", \"af_quantity\" :\"1\" }"
app_version_name Não

Your app version or identifier.

  • Format: String
  • Example: "app_version_name": "my_app_version"
Data/hora do evento Não

The time the event occurred using UTC timezone.

  • Default: If noeventTimeis sent, the time is set to the HTTP message arrival time.
  • Formato: na sequência aaaa-mm-dd hh:mm:ss.sss a hora deve estar no fuso horário 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.
  • Eventos com horário futuro, ou seja, após o horário de chegada, são carimbados com o horário de chegada. 


  • Os horários estão em UTC
  • Um evento é enviado com eventTime = Segunda-feira 21:00.
Hora de chegada Time recorded by AppsFlyer Observação
Terça-feira 01:00 Segunda-feira 21:00 Arrived before close of business.
Quarta-feira 09:00  Quarta-feira 09:00 Arrived after close of business. Time is set to arrival time. 
eventCurrency Não

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

Padrão: USD 

Example: "eventCurrency": "ZAR"

Parâmetros do 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 O que fazer
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 Falha ao autenticar Ensure that the authentication key is correct.
400  appsflyer_id é um campo obrigatório
  • 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  Payload faltando ou que falhou no processamento
  • Certifique-se de que o JSON esteja em formato de string e formatado corretamente.
  • If more than one event is included in the JSON payload. Make sure to send one event per request.
500  Erro interno do 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

Diferença entre orgânico e não orgânico

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.

O que isso significa é que alguns dados que a AppsFlyer associa a eventos in-app S2S não orgânicos não estão associados a eventos in-app S2S orgânicos.


Por exemplo, se você comparar relatórios de dados brutos de eventos in-app S2S não orgânicos e orgânicos, os eventos não orgânicos contêm dados que os eventos in-app não orgânicos não contêm.

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:


Ao testar mensagens S2S, se você estiver usando dados brutos ou APIs push ou pull, procure o registro com fonte de mídia "s2s_test". Esse é o seu dispositivo de teste e essa ID de dispositivo AppsFlyer é a ID que você precisa.

Envio de receitas negativas

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

Se você quiser enviar eventos sem o valor do evento, é só passar uma string vazia ao valor do evento: "event_value":""

Em seguida, o AppsFlyer é capaz, de acordo com a configuração do anunciante, de enviar esses eventos avançados no aplicativo para fontes de mídia para fins de direcionamento avançado, otimização e criação de público.

Solução 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
Este artigo foi útil?