Serving Protocol
The wire contract every Appylar SDK speaks.
Applies to Appylar v1.0
The Appylar serving protocol is the wire contract every Appylar SDK speaks to the data-plane API — the same for the Android, iOS and Flutter SDKs today and for React Native, Unity and any future SDK. It is deliberately small, additive and versioned so it can grow for years without breaking older apps. This page is the canonical reference; the per-SDK guides (Android, iOS, Flutter) show how each one implements it.
The current protocol version is 1. Every SDK at v1.1.0 or later speaks it.
#Principles
- Identical across SDKs. Same endpoints, same header names, same field names, same semantics. An SDK is a thin, faithful client of this protocol — it never invents its own dialect.
- The SDK reports facts; the backend owns policy. The SDK sends what it reliably knows (the operating system it is running on) and what the developer explicitly declared (a non-default store). How those map to a store listing is decided server-side, so routing policy can change without shipping new SDKs.
- Nothing derivable is sent. If the backend can determine something itself, the SDK does not send it.
- Additive and backward compatible. New optional fields and new enum values never break older SDKs or older backends. Only a breaking change to existing semantics bumps the protocol version.
#Transport
- HTTPS + JSON, under the API base
https://api.appylar.com/v1. The/v1is the API version (routes and resources); the protocol version below is a separate, finer-grained contract carried in a header. - Bearer auth:
/tokenis exchanged for a short-lived serving token;sdk/config,serveandeventssendAuthorization: Bearer <serving-token>.
#Headers
Every data-plane request (/token, /sdk/config, /serve, /events) carries:
| Header | Example | Meaning |
|---|---|---|
Appylar-Protocol | 1 | The protocol version the SDK implements. Absent ⇒ treated as legacy (v0). |
Appylar-SDK | android-sdk/1.1.0 | SDK identity as <sdk>/<version>. sdk ∈ android-sdk, ios-sdk, flutter-sdk, react-native-sdk, unity-sdk, … |
Appylar-SDK is how the backend observes which SDK and version is calling — without ever persisting that on the app. (An app's framework and SDK are not stored; they are observed here.)
The identifier always carries the -sdk suffix (flutter-sdk, not flutter). This names the official Appylar SDK implementation, keeping it unambiguous in logs, analytics and support: flutter-sdk is clearly the Appylar SDK rather than the Flutter framework, and it never collides with the runtime os values (android/ios) sent on /serve. New SDKs follow the same rule — react-native-sdk, unity-sdk.
#The /serve request
POST /v1/serve — the only request that carries serving inputs. All fields are optional on the wire (for backward compatibility); a protocol-v1 SDK always sends os.
{
"device_id": "b1f2…", // client-generated id for frequency capping
"os": "android", // runtime OS: "android" | "ios"
"store": "amazon_appstore" // optional: explicit distribution store (see below)
}
#Fields — and why each exists
| Field | Required | Why it exists | Why it can't be derived |
|---|---|---|---|
device_id | Recommended | Frequency capping per device/session. | Generated on the client; the backend has no other stable per-device signal. |
os | Sent by every v1 SDK | The primary store-routing signal, and a future hook for OS-specific formats/analytics. | One app key is shared across OSes (a Flutter app is one app on both), so the backend cannot infer the OS from the token. |
store | Optional | Declares the exact store a build ships to when it isn't the OS default (Amazon Appstore, Huawei AppGallery, Samsung Galaxy Store…). | A build/distribution fact known only to the developer; an "Android" build could come from any of several stores. |
#Deliberately not in the protocol
Challenged and removed, because each is derivable or unused: device model, screen size, OS version, locale, network type, IP/geo (derived server-side), and the app's framework/SDK (observed via the Appylar-SDK header, not a body field). Adding any of these later is a purely additive change if a real need appears.
#Store routing
The backend resolves a served ad's click-through to a specific store listing using the hints above, in priority order:
- Explicit
store— the listing for exactly that store, if the promoted app has one. - OS default store —
android→ Google Play,ios→ Apple App Store. - OS-consistent listing — any listing whose store belongs to the viewer's OS (e.g. an Android viewer of an app listed only on Amazon).
- First listing — a deterministic fallback.
- None — if the app has no listings, the ad serves without a destination.
So the common case needs only os; store exists for the multi-store-per-OS world:
| OS | Default store | Override with store when the build ships to… |
|---|---|---|
android | Google Play | amazon_appstore, huawei_appgallery, samsung_galaxy_store |
ios | Apple App Store | (App Store is the only iOS store) |
Because the OS→store default lives on the server, changing it never requires an SDK release.
#Versioning & extension
- Version signal: the
Appylar-Protocolinteger header. This is v1. - Additive changes (no version bump): new optional request fields, new response fields, new
storeenum values. Old SDKs omit them; old backends ignore them. - Breaking changes (version bump): changing the meaning of an existing field, or making a field mandatory. The backend supports a window of versions; SDKs advertise theirs via the header so the backend can adapt.
- Introspection:
GET /v1/sdk/configreturnsprotocol_version, so tools and SDKs can discover the backend's current version.
#Backward compatibility
- Legacy SDKs (v1.0.0, pre-protocol). They send no protocol headers and only
device_id. The backend treats a missingAppylar-Protocolas v0 and serves normally; store routing falls back to the app's first listing. Nothing breaks. - New SDK, old backend. The extra headers and fields are ignored by a backend that doesn't know them. Nothing breaks.
- Guarantee: no field in this protocol is ever mandatory on the wire, so a correct request from any protocol version is always served.
#Implementation checklist (for a new SDK)
- Send
Appylar-Protocol: 1andAppylar-SDK: <platform>/<version>on every data-plane request. - On
/serve, senddevice_id, the runtimeos, andstorewhen the developer configured one. - Expose an
AppylarStoretype (the five wire values) and an optionalstoreon the SDK's config. - Never send derivable/unused data.
- Add tests asserting the exact headers and serve body.