Dynalinks
Using Dynalinks API

iOS and Android apps API

Your project's iOS and Android apps tell Dynalinks which app should open your links. They drive the apple-app-site-association and assetlinks.json files served on your domain, and the App Store and Google Play redirects for users who do not have the app installed.

You can manage them in the console, or with the endpoints on this page. They are useful when you provision projects automatically, rotate an Android signing certificate, or keep app settings in sync with your own configuration.

These endpoints follow the conventions described in API basics: the same base URL, authentication, pagination and error format.


Before you start

  • Use a project API key from Settings > API Keys. The apps belong to that key's project.
  • If the key has custom scopes, it needs the matching ones: ios_apps.index, ios_apps.show, ios_apps.create, ios_apps.update, ios_apps.destroy, and the same five for android_apps.
  • Reading apps requires Viewer access to the project. Creating, updating and deleting apps requires Manager or Owner access. See Team members & roles.

Endpoints

MethodPathDescription
GET/api/v1/ios_appsList iOS apps
POST/api/v1/ios_appsCreate an iOS app
GET/api/v1/ios_apps/:idGet one iOS app
PATCH/api/v1/ios_apps/:idUpdate an iOS app
DELETE/api/v1/ios_apps/:idDelete an iOS app
GET/api/v1/android_appsList Android apps
POST/api/v1/android_appsCreate an Android app
GET/api/v1/android_apps/:idGet one Android app
PATCH/api/v1/android_apps/:idUpdate an Android app
DELETE/api/v1/android_apps/:idDelete an Android app

:id is the app's id (a UUID) from the create or list response, not its bundle ID or package name.


iOS apps

Fields

FieldRequiredDescription
bundle_idYesYour app's bundle identifier, for example com.example.app. Must be unique within the project.
team_idYesYour 10-character Apple Developer Team ID.
app_store_idNoThe numeric App Store ID, used to send users without the app to the App Store. Anything that is not a digit is removed, so id1234567890 is stored as 1234567890.
nameNoA display name for the app.
clip_bundle_idNoYour App Clip's bundle identifier, if you have one. If you include the Team ID prefix (ABCDE12345.com.example.app.Clip), it is removed.
custom_schemeNoYour app's custom URL scheme, for example myapp. A trailing :// is removed. Used in the app link metadata of your link preview pages.

The response also contains id.

Create an iOS app

Send the fields inside an ios_app object:

curl --request POST https://dynalinks.app/api/v1/ios_apps \
  --header 'Authorization: Token YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "ios_app": {
      "name": "Example",
      "bundle_id": "com.example.app",
      "team_id": "ABCDE12345",
      "app_store_id": "1234567890"
    }
  }'

Response (201 Created):

{
  "id": "6b1f2c3d-8e4a-4f5b-9c7d-2a1e0f3b4c5d",
  "name": "Example",
  "bundle_id": "com.example.app",
  "team_id": "ABCDE12345",
  "app_store_id": "1234567890",
  "clip_bundle_id": null,
  "custom_scheme": null
}

Update an iOS app

Send only the fields you want to change. Fields you leave out keep their current value.

curl --request PATCH https://dynalinks.app/api/v1/ios_apps/6b1f2c3d-8e4a-4f5b-9c7d-2a1e0f3b4c5d \
  --header 'Authorization: Token YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"ios_app": {"custom_scheme": "exampleapp"}}'

The response (200 OK) contains the full updated app.


Android apps

Fields

FieldRequiredDescription
package_nameYesYour app's package name (applicationId), for example com.example.app. Must be unique within the project.
fingerprintsYesAn array of SHA-256 signing certificate fingerprints, at least one.
nameNoA display name for the app.
custom_schemeNoYour app's custom URL scheme, for example myapp. A trailing :// is removed. Used in the app link metadata of your link preview pages.

The response also contains id.

Each fingerprint must be written as pairs of hexadecimal characters separated by colons, the format printed by keytool and ./gradlew signingReport:

14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5

Fingerprints are stored in upper case, and empty entries are dropped.

Important

If you use Play App Signing, Google re-signs your app before distributing it. Add the app signing key fingerprint from the App integrity page of the Play Console, not only your upload key. You can list several fingerprints, for example for both your debug and release builds.

Create an Android app

Send the fields inside an android_app object:

curl --request POST https://dynalinks.app/api/v1/android_apps \
  --header 'Authorization: Token YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "android_app": {
      "name": "Example",
      "package_name": "com.example.app",
      "fingerprints": [
        "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
      ]
    }
  }'

Response (201 Created):

{
  "id": "0e9d8c7b-6a5f-4e3d-8c2b-1a0f9e8d7c6b",
  "name": "Example",
  "package_name": "com.example.app",
  "fingerprints": [
    "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
  ],
  "custom_scheme": null
}

Update an Android app

Send only the fields you want to change. When you send fingerprints, the array replaces the existing list, so include every fingerprint you want to keep:

curl --request PATCH https://dynalinks.app/api/v1/android_apps/0e9d8c7b-6a5f-4e3d-8c2b-1a0f9e8d7c6b \
  --header 'Authorization: Token YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "android_app": {
      "fingerprints": [
        "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5",
        "A1:B2:C3:D4:E5:F6:07:18:29:3A:4B:5C:6D:7E:8F:90:A1:B2:C3:D4:E5:F6:07:18:29:3A:4B:5C:6D:7E:8F:90"
      ]
    }
  }'

The response (200 OK) contains the full updated app.


Listing apps

curl https://dynalinks.app/api/v1/ios_apps \
  --header 'Authorization: Token YOUR_API_KEY'
{
  "ios_apps": [
    {
      "id": "6b1f2c3d-8e4a-4f5b-9c7d-2a1e0f3b4c5d",
      "name": "Example",
      "bundle_id": "com.example.app",
      "team_id": "ABCDE12345",
      "app_store_id": "1234567890",
      "clip_bundle_id": null,
      "custom_scheme": "exampleapp"
    }
  ],
  "meta": { "page": 1, "limit": 20, "count": 1, "last": 1, "from": 1, "to": 1 }
}

GET /api/v1/android_apps works the same way and returns an android_apps array.

Lists support page and limit (see Pagination) and filters in the q parameter (see Filtering and sorting). You can filter on these fields:

iOS appsAndroid apps
name, bundle_id, team_id, clip_bundle_id, custom_scheme, created_atname, package_name, custom_scheme, created_at

For example, to find an app by its bundle ID:

curl --get https://dynalinks.app/api/v1/ios_apps \
  --header 'Authorization: Token YOUR_API_KEY' \
  --data-urlencode 'q[bundle_id_eq]=com.example.app'

Getting one app

curl https://dynalinks.app/api/v1/android_apps/0e9d8c7b-6a5f-4e3d-8c2b-1a0f9e8d7c6b \
  --header 'Authorization: Token YOUR_API_KEY'

Returns the app (200 OK), or 404 Not Found if the id does not exist in the key's project.


Deleting an app

curl --request DELETE https://dynalinks.app/api/v1/ios_apps/6b1f2c3d-8e4a-4f5b-9c7d-2a1e0f3b4c5d \
  --header 'Authorization: Token YOUR_API_KEY'

A successful delete returns 204 No Content with an empty body.

Warning

Links that used the deleted app are kept, but they are no longer associated with any app. They stop opening that app and stop redirecting to its store page until you assign another app to them. The app is also removed from your domain's association file, so iOS or Android stops treating your links as belonging to it.


Validation errors

When the data is invalid, the API responds with 422 Unprocessable Content and lists every problem in a single error message:

{
  "error": "Bundle is invalid, Bundle can't be blank, and Team is the wrong length (should be 10 characters)"
}

Common messages:

MessageCause
Bundle can't be blankbundle_id is missing or empty.
Bundle is invalidbundle_id is not a valid bundle identifier. Use letters, digits, hyphens and dots.
Bundle has already been takenThe project already has an iOS app with this bundle ID. Update that app instead.
Team is the wrong length (should be 10 characters)team_id is missing or is not exactly 10 characters.
Clip bundle is invalidclip_bundle_id is not a valid bundle identifier.
Package name can't be blankpackage_name is missing or empty.
Package name is invalidpackage_name is not a valid Android package name. It needs at least two segments separated by dots, each starting with a letter (for example com.example).
Package name has already been takenThe project already has an Android app with this package name.
Fingerprints can't be blankfingerprints is missing or contains no non-empty values.
Fingerprints is invalidA fingerprint is not written as colon-separated hexadecimal pairs. Fingerprints without colons are rejected.

Other errors:

  • 400 Bad Request if the body has no ios_app / android_app object, the body is not valid JSON, or Content-Type is not application/json.
  • 401 Unauthorized if the key is invalid, lacks the required scope, or its owner does not have the required role.
  • 404 Not Found with {"error": "iOS app not found"} or {"error": "Android app not found"} for an unknown id.
  • 429 Too Many Requests if you exceed the rate limit of 600 create, update and delete requests per minute (shared per key for iOS apps, and separately for Android apps).

On this page