Click signing for ad networks

At a glance: Add signature validation to clicks to avoid fraud liabilities and so fraudulent clicks aren't attributed to your ad network. 

About click signing

With minimal tech, fraudsters can send clicks on behalf of an ad network and create thousands, or even millions, of fake clicks that get sent to AppsFlyer. Sometimes, the ad networks themselves aren't aware of the issue. 

In order for an ad network to ensure that clicks attributed to it by AppsFlyer actually originated from the ad network, and not from a fraudster creating fake clicks, ad networks should sign their clicks with HMAC-SHA256 signatures.

Click signing also prevents the ad network’s traffic from being blocked due to click flooding. Meaning, if an ad network reaches the click blocking threshold due to an extreme level of click flooding, AppsFlyer stops recording and attributing their clicks for the rest of the day.

The signatures enable AppsFlyer to validate the clicks and make sure that the click information hasn't been manipulated by fraudsters.

  • Validated clicks are recorded, and attributed to the ad network 
  • Invalidated clicks are rejected and: 
    • Are aggregated in Protect360 reports made available to the ad network (not advertisers).
    • Do not impact the conversion rate or click blocking threshold of the ad network

Click signature integration

Flow

The following chart outlines the flow from initial development and basic test, to production tests, and finally, to production. 

Click_signing_integration_flow.png

Procedure

To sign your clicks:

  1. Generate a secret key with the Generate secret key API.
    Best practice: Generate and use a new secret key every 24 hours, with an expiration of 36 hours. 
  2. Develop code in your servers that calls the Generate secret key API, takes the secret key and generates an HMAC-SHA256 signature. See code sample.
    You can use the other APIs as described in the table that follows.
  3. The code adds the following to your click URLs: 
    • An expires parameter that contains a Unix timestamp (in UTC) after which the ad network doesn't claim the click.
    • The HMAC-SHA256 signature.
    • Example:

      https://app.appsflyer.com/com.app.id?pid=adnetwork_int&c=my_campaign&clickid=sdkfjasksjskdfj9845weh&af_site_id=12345
      &expires=1597657118&signature=8fnDVzZP_WRZnv3KNJaREOEfvB5p9oRc_XlKEvUo8gk

Click signature APIs

AppsFlyer provides APIs that allow ad networks to manage the click signing process. See the list of APIs in the table below, and the sections that follow with information necessary for using the API. 

Click signature APIs
API method Remarks
Generate secret key Generate secret keys to be used in the signature.
Revoke secret key Cancel compromised secret keys.
Test Send single clicks for testing the signature.
Configure mode

Configure mode of click signing mode: 

  • disabled (default): No click signature validation is done.
  • report-only (testing mode): AppsFlyer validates click signatures, but doesn't block clicks with invalid signatures. The ad network can use the Report API to get statistics on successful/failing clicks.
  • enabled: AppsFlyer blocks clicks with invalid or missing signatures.
Configure circuit breaker

Configure the mode of the circuit breaker that protects the ad network from having too many blocked clicks:

  • enabled (default): If the Protect360 system detects that too many clicks are marked as invalid, then in order to protect the ad network from having potentially incorrectly blocked clicks:
    • Configure mode API changes to report-only.
    • And email alert is sent to the ad network to verify the click signing API is correctly set up.
  • disabled: Protect360 continues to block all clicks marked as invalid, even when the block percentage is unusually high.
Get configuration

Get the mode and IDs of active secret keys.

Report

Get statistics on successful and failing clicks when the system is in report-only or enabled mode.

Exclude app Configure app IDs to be excluded from click signing. 
Remove excluded app Configure app IDs to be included in click signing after being excluded.

Generate secret key method

Generate secret key basics

Category Item

Description

Request HTTP method POST
Path

https://hq1.appsflyer.com/api/p360-click-signing/secret?ttlHours=<ttlHours>

Authorization header 
Response Results Secret key returns in a JSON
  Request limit Maximum of 2 active secret keys at a time

API request

Method

POST https://hq1.appsflyer.com/api/p360-click-signing/secret?ttlHours=<ttlHours>
Parameters

Parameter

Description
ttlHours
  • Time to live for the secret key, in hours, from 1-1440 hours.
  • Default is 36 hours.

JSON response

Key

Description

secret-key-id

An ID for the secret key

secret key

The secret key for the click signature

expiration

Epoch time in milliseconds

Generate secret key curl example and response

Curl request


curl --location --request POST 'https://hq1.appsflyer.com/api/p360-click-signing/secret?ttlHours=36' \
-H 'Authorization: Bearer {API V2.0 token available to the admin in the dashboard.}'

JSON response

{
	"secret-key-id": "59ad6547-affc-45eb-a6c9-9805f88ee755",
	"secret-key": "zGW6Rhrmb8+vuhHtL/Kp6rW5Ci9PNsjH1J5MGO9SIeg=",
	"expiration": 1610533263
}

HTTP response codes

Response codes

Code 

Message

Remarks

200 OK

 

401 Not authorized

Invalid or missing authorization header

Revoke secret key method

Revoke secrete key basics

Category Item

Description

Request HTTP method DELETE
Path

https://hq1.appsflyer.com/api/p360-click-signing/secret/<secret-id>

Authorization header 
Response Results Empty

API request

Method

DELETE https://hq1.appsflyer.com/api/p360-click-signing/secret/<secret-id>
Parameters

Parameter

Description
secret-id

The ID of the secret key to be revoked

Generate secret key curl example and response

Curl request


curl --location --request DELETE 'https://hq1.appsflyer.com/api/p360-click-signing/secret/59ad6547-affc-45eb-a6c9-9805f88ee755' \
-H 'Authorization: Bearer {API V2.0 token available to the admin in the dashboard.}'

HTTP response codes

Response codes

Code 

Message

Remarks

200 OK

 

401 Not authorized

Invalid or missing authorization header

Test method

Test basics

Category Item

Description

Request HTTP method POST
Path

https://hq1.appsflyer.com/api/p360-click-signing/test

Authorization header 
Response Results Returns in a JSON

API request

Method

POST https://hq1.appsflyer.com/api/p360-click-signing/test
Parameters

Parameter

Description
url

The click URL (including signature) to test

JSON response

Key

Description

test-status

Either Passed or Failed

message

Reason for the test failure. For example:

  • Missing signature
  • Invalid signature
  • Expired 

Test curl example and response

Curl request


curl --location --request POST 'https://hq1.appsflyer.com/api/p360-click-signing/test' \
-H 'Authorization: Bearer {API V2.0 token available to the admin in the dashboard.}' \
--header 'Content-Type: application/json' \
--data-raw '{
   "url": "https://app.appsflyer.com/com.app.id?pid=adnetwork_int&c=my_campaign&clickid=sdkfjasksjskdfj9845weh&af_site_id=12345&expires=1597657118&signature=8fnDVzZP_WRZnv3KNJaREOEfvB5p9oRc_XlKEvUo8gk"
}'

JSON response

{
	"test-status":"Passed / Failed",
	"message": "Invalid signature"
}

HTTP response codes

Response codes

Code 

Message

Remarks

200 OK

 

401 Not authorized

Invalid or missing authorization header

Configure mode method

Configure mode basics

Category Item

Description

Request HTTP method POST
Path

https://hq1.appsflyer.com/api/p360-click-signing/config/mode/<mode>

Authorization header 
Response Results Returns in a JSON

API request

Method

POST https://hq1.appsflyer.com/api/p360-click-signing/config/mode/<mode>
Parameters

Parameter

Description
mode

Options:

  • enabled
    Note! Only set the mode to "enabled" after you run in report-only mode for few hours and check your reports to ensure the configuration is correct and that all clicks passed signature validation.
  • disabled
  • report-only

Configure mode curl example

Curl request


curl --location --request POST 'https://hq1.appsflyer.com/api/p360-click-signing/config/mode/report-only' \
-H 'Authorization: Bearer {API V2.0 token available to the admin in the dashboard.}'

HTTP response codes

Response codes

Code 

Message

Remarks

200 OK

 

400 Bad request

Invalid mode

401 Not authorized

Invalid or missing authorization header

Configure circuit breaker method

Configure circuit breaker basics

Category Item

Description

Request HTTP method POST
Path

https://hq1.appsflyer.com/p360-click-signing/config/circuit-breaker

Authorization header 
Response Results HTTP status

API request

Method

POST https://hq1.appsflyer.com/p360-click-signing/config/circuit-breaker
Request body JSON

Parameter

Description
status
  • enabled
  • disabled

Configure circuit breaker curl example and response

Curl request


curl --location --request POST 'https://hq1.appsflyer.com/api/p360-click-signing/config/circuit-breaker' \
-H 'Authorization: Bearer {API V2.0 token available to the admin in the dashboard.}'
--data-raw '{
"status":"enabled"
}'

HTTP response codes

Response codes

Code 

Message

Remarks

200 OK

 

400 Bad request

Invalid status

401 Not authorized

Invalid or missing authorization header

Get configuration method

Get configuration basics

Category Item

Description

Request HTTP method GET
Path

https://hq1.appsflyer.com/api/p360-click-signing/config

Authorization header 
Response Results Returns in a JSON

API request

Method

GET https://hq1.appsflyer.com/api/p360-click-signing/config

JSON response

Key

Description

mode

One of:

  • enabled
  • disabled
  • report-only
circuit-breaker-config

A JSON object containing status, one of:

  • enabled
  • disabled

active-key-ids

A JSON array holding active keys:

  • secret-key-id: Randomly-generated ID of the secret key
  • expiration: Epoch time in milliseconds of the secret key

excluded-app-ids

A JSON array with excluded app-ids

Get configuration curl example and response

Curl request


curl --location --request GET 'https://hq1.appsflyer.com/api/p360-click-signing/config' \
-H 'Authorization: Bearer {API V2.0 token available to the admin in the dashboard.}'

JSON response

{
	"mode": "report-only",
	"active-key-ids": [
		{
			"secret-key-id": "59ad6547-affc-45eb-a6c9-9805f88ee755",
			"expiration": 1610533263
		}
	],
	"excluded-app-ids": [
		"app-id-1", 	"app-id-2"
	]

}

HTTP response codes

Response codes

Code 

Message

Remarks

200 OK

 

401 Not authorized

Invalid or missing authorization header

Report method

Report basics

Category Item

Description

Request HTTP method GET
Path

https://hq1.appsflyer.com/api/p360-click-signing/report

Authorization header 
Response Results Returns in a CSV

API request

Method

GET https://hq1.appsflyer.com/api/p360-click-signing/report
Parameters

Parameter

Description
start-date

Start date and time for the report. Format: yyyy-mm-ddThh

end-date

End date and time for the report. Format: yyyy-mm-ddThh

The API requires either both start-date and end-date or neither of them. If start/end date are not provided, the report shows results for the past 24 hours.

CSV response

Column

Description

time

Date and time of the clicks. Format yyyy-mm-ddThh

total_clicks

Total number of clicks during the reporting period

valid_clicks

Number of valid clicks during the reporting period

missing_signature

Number of clicks missing signatures during the reporting period

expired_clicks

Number of expired clicks during the reporting period

invalid_signature

Number of clicks with invalid signatured during the reporting period

no_active_secrets

Number of clicks rejected because there are no active secret keys in the system (usually when the system is in report-only mode)

Test curl example and response

Curl request


curl --location --request GET 'https://hq1.appsflyer.com/api/p360-click-signing/report?start-date=2021-01-07T07&end-date=2021-01-17T12' \
-H 'Authorization: Bearer {API V2.0 token available to the admin in the dashboard.}' \

CSV response

time

total_clicks

valid_clicks

missing_signature

expired_clicks

invalid_signature

no_active_signatures

2021-01-17T07

928082156

928082156

 0

 0

 0

 0

2021-01-17T08

923796132

923796132

 0

 0

 0

 0

2021-01-17T09

917541373

917541373

 0

 0

 0

 0

2021-01-17T10

909977064

909977064

 0

 0

 0

 0

2021-01-17T11

965104299

965104299

 0

 0

 0

 0

2021-01-17T12

975134824

975134824

 0

 0

 0

 0

HTTP response codes

Response codes

Code 

Message

Remarks

200 OK

 

401 Not authorized

Invalid or missing authorization header

Exclude app method

Exclude app method basics

Category Item

Description

Request HTTP method POST
Path

https://hq1.appsflyer.com/api/p360-click-signing/config/excluded-app/<app-id>

Authorization header 
Response Results Empty

API request

Method

POST https://hq1.appsflyer.com/api/p360-click-signing/config/excluded-app/<app-id>
Parameters

Parameter

Description
app-id

Application ID to be excluded from click signing validation

Exclude app curl example

Curl request


curl --location --request POST 'https://hq1.appsflyer.com/api/p360-click-signing/config/excluded-app/appname.com' \
-H 'Authorization: Bearer {API V2.0 token available to the admin in the dashboard.}'

HTTP response codes

Response codes

Code 

Message

Remarks

200 OK

 

401 Not authorized

Invalid or missing authorization header

Remove excluded app method

Remove excluded app method basics

Category Item

Description

Request HTTP method DELETE
Path

https://hq1.appsflyer.com/api/p360-click-signing/config/excluded-app/<app-id>

Authorization header 
Response Results Empty

API request

Method

DELETE https://hq1.appsflyer.com/api/p360-click-signing/config/excluded-app/<app-id>
Parameters

Parameter

Description
app-id

Application ID to be removed from the list of apps excluded from click signing validation

Remove excluded app curl example

Curl request


curl --location --request DELETE 'https://hq1.appsflyer.com/api/p360-click-signing/config/excluded-app/appname.com' \
-H 'Authorization: Bearer {API V2.0 token available to the admin in the dashboard.}'

HTTP response codes

Response codes

Code 

Message

Remarks

200 OK

 

401 Not authorized

Invalid or missing authorization header

Code sample

package sign;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

.
.
.

String clickUrl = "https://app.appsflyer.com/com.app.id?pid=adnetwork_int&c=my_campaign&clickid=sdkfjasksjskdfj9845weh&af_site_id=12345";
String secretKey = "secret_key"; 
int ttlMinutes = 5;


//add expiration to the click URL
long expiration = System.currentTimeMillis() + (60000L * ttlMinutes);
clickUrl += "&expires="+expiration;

//create a SecretKey object from the given key string
SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);

//generate a signature from the click url and encode it with base64 without padding
String generatedSignature =     
Base64.getUrlEncoder().withoutPadding().encodeToString(mac.doFinal(clickUrl.getBytes()));

//add the signature to the click URL
String signedClickUrl = clickUrl + "&signature=" + generatedSignature;

Traits

Traits

Trait

Description

Click signature The signature must occur on the ad network servers.
Secret key
  • Ad networks can have a maximum of two secret keys active simultaneously.
  • Each secret key has an expiration.
  • Clicks signed with expired secret keys are rejected.
Report API Updated statistics of click validity are aggregated on an hourly basis.
Was this article helpful?