Drıftstack docs
Docs

Device profiles

A device profile (archetype in the API) is the exact iPhone model, iOS version, and browser version that a session or saved profile is created with. Do not hard-code a copied catalog: use this endpoint to populate selectors and validate configuration. It is the same list the desktop app and dashboard use.

List available device profiles

GET /v1/archetypes

No API key is required. Responses are cacheable for five minutes: Cache-Control: public, max-age=300.

curl https://api.driftstack.dev/v1/archetypes

Response (200):

{
  "default_archetype_id": "iphone15_ios18_7_safari26_5",
  "data": [
    {
      "id": "iphone15_ios18_7_safari26_5",
      "display_label": "iPhone 15 / iOS 18.7 / Safari 26.5",
      "device": "iPhone 15",
      "ios_version": "18.7",
      "safari_version": "26.5",
      "browser": "safari",
      "browser_version": "26.5",
      "status": "available",
      "new_profiles": "open",
      "is_default": false
    }
  ]
}

Only customer-selectable entries are returned:

  • launch — selectable (the original launch device)
  • available — selectable

Each entry says whether new profiles may use it:

  • new_profiles — open or closed. A new profile (POST /v1/profiles, a copy, an import, a restore from a snapshot) or a session without a profile may name only an open device. A closed device stays in the list because the profiles already on it keep it and keep running; naming it for a new one is refused with 400 (code: "device_closed_for_new_profiles"; see Errors). An entry without new_profiles is from an older server: treat it as open.

Each entry says which browser it runs:

  • browser — currently safari or chrome (Chrome for iPhone). Every entry today is safari. It is an open string: later versions may add values, and other means a browser this server cannot name yet. Treat a value you do not know as another browser, not as an error.
  • browser_version — the version of that browser. On a safari entry it is the same value as safari_version ("26.4"). On a chrome entry it is the Chrome version (for example "149"). An other entry has none.
  • safari_version — always a Safari release, on every entry. On a chrome entry it is the Safari release that device’s iOS ships, which Chrome for iPhone is built on (for example "26.6"); it is never the Chrome version.

browser and browser_version were added after safari_version. A client that does not read them keeps working. An entry without browser is from an older server: treat an entry without browser as safari.

There is no default device. default_archetype_id and is_default are deprecated: they stay in the response for clients that read them, is_default is false on every entry, and default_archetype_id names one model open for new profiles (null when none is open) but is not what a create gets; ignore it. New profiles and sessions started without a device get a randomly chosen iPhone model from those your plan includes that are open for new profiles; you can also pick a model yourself. Open means new_profiles is open in the list above. A profile created without archetype keeps the model it gets, drawn once. A session created without a profile or archetype runs as a fresh visitor, a different model from session to session, weighted toward the newest iOS; pass archetype to pin a device, or profile_id to keep an identity between runs. The session’s archetype and archetype_source (explicit, profile or random) say what it got. Every paid plan includes every device. The free trial includes the iPhone 13 and iPhone 13 mini only; any other device is refused with 403.

Direct session creation, profile creation, and profile import accept only an id present in the current response. Any other id returns 400 ValidationFailed on the archetype field before anything is created.

Which devices can start right now

GET /v1/archetypes/availability

Requires an API key; it answers for the key’s own account. Never cached: Cache-Control: private, no-store. The list above says which devices you may choose; this says, right now, whether a machine that runs sessions is running each one.

curl https://api.driftstack.dev/v1/archetypes/availability \
  -H "Authorization: Bearer $DRIFTSTACK_API_KEY"

Response (200):

{
  "data": [{ "id": "iphone15_ios18_7_safari26_5", "available_now": true }]
}

One row per entry of GET /v1/archetypes, in the same order:

  • available_now: true — a machine is running this device now.
  • available_now: false — no machine is running it right now. A profile create (POST /v1/profiles, POST /v1/profiles/import) or a session create on it (POST /v1/sessions, POST /v1/profiles/{id}/launch, POST /v1/agent-sessions) is refused (after any device, plan or closed-device refusal) with 409 device-unavailable, code: "archetype_not_served", retryable: true, and nothing is created. Choose another device, or try again later.
  • available_now: null — not known right now (no machine is connected, one has not reported yet, or this check is not on for your account). A create goes ahead as usual.

A create that names no device is not given one whose available_now is false while another device on your plan can start.

Generate a create payload from the live catalog

Resolve capabilities to an id at runtime instead of constructing or guessing a slug. Match on browser as well as the versions: a chrome entry carries the Safari release it is built on in safari_version, so a filter on safari_version alone can match a Chrome entry. This helper can generate the body for either POST /v1/sessions or POST /v1/profiles:

type ArchetypeFilter = {
  id?: string;
  device?: string;
  ios_version?: string;
  safari_version?: string;
  // 'safari' unless you ask for another browser.
  browser?: string;
};

async function archetypeCreatePayload(filter: ArchetypeFilter = {}) {
  const requested = Object.entries(filter).filter(([, value]) => value !== undefined);
  if (requested.length === 0) return {};

  const response = await fetch('https://api.driftstack.dev/v1/archetypes');
  if (!response.ok) throw new Error(`Catalog request failed: ${response.status}`);

  const catalog = (await response.json()) as {
    default_archetype_id: string | null; // deprecated: there is no default device
    data: Array<{
      id: string;
      device: string;
      ios_version: string;
      safari_version: string;
      browser?: string;
    }>;
  };
  const matches = catalog.data.filter(
    (entry) =>
      (filter.id === undefined || entry.id === filter.id) &&
      (filter.device === undefined || entry.device === filter.device) &&
      (filter.ios_version === undefined || entry.ios_version === filter.ios_version) &&
      (filter.safari_version === undefined || entry.safari_version === filter.safari_version) &&
      // An entry without `browser` is from an older server: Safari.
      (entry.browser ?? 'safari') === (filter.browser ?? 'safari'),
  );

  if (matches.length === 0) {
    throw new Error('No currently selectable archetype matches those capabilities.');
  }
  if (matches.length > 1) {
    throw new Error(
      'More than one archetype matches. Add another capability or use an exact catalog id.',
    );
  }
  return { archetype: matches[0]!.id };
}

const sessionBody = await archetypeCreatePayload({
  device: 'iPhone 15',
  ios_version: '18.7',
  safari_version: '26.5',
});

If no capability filter is needed, omit archetype. New profiles and sessions started without a device get a randomly chosen iPhone model from those your plan includes that are open for new profiles; you can also pick a model yourself. Existing stored profiles keep their pinned device profile even if it later leaves the selectable catalog; compatibility operations on those profiles remain available, but new direct creates and imports must choose from the live response.

The route and its inline response schema are also published in /openapi.json. Do not copy returned IDs into a separate application constant; refresh the catalog after its five-minute response lifetime.