# Start a session without an account

Proves with App Attest that the request comes from the switchfile app on a real device. A new key sends its attestation once; later tokens need an assertion of the same key. The token converts files with the free files of the day of this device. Buying credits needs an account.

`POST https://api.switchfile.io/v1/app/guest`

No API key needed.

## Example

cURL:

```bash
curl "https://api.switchfile.io/v1/app/guest" \
  -H "Content-Type: application/json" \
  -d '{
    "key_id": "kWQ0m6p1y4s3Vb2n8c7x5z9A0Bq1Cw2Dv3Eu4Ft5Gs8=",
    "challenge": "1763116800.q8Jx2v3mQ0yS6nB1kR9tLw.Wm9vYmFyYmF6cXV4",
    "attestation": "o2NmbXRvYXBwbGUtYXBwYXR0ZXN0…"
  }'
```

Node.js:

```js
const res = await fetch("https://api.switchfile.io/v1/app/guest", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    key_id: "kWQ0m6p1y4s3Vb2n8c7x5z9A0Bq1Cw2Dv3Eu4Ft5Gs8=",
    challenge: "1763116800.q8Jx2v3mQ0yS6nB1kR9tLw.Wm9vYmFyYmF6cXV4",
    attestation: "o2NmbXRvYXBwbGUtYXBwYXR0ZXN0…"
  }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
console.log(await res.json());
```

PHP:

```php
<?php
$ch = curl_init('https://api.switchfile.io/v1/app/guest');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode([
        'key_id' => 'kWQ0m6p1y4s3Vb2n8c7x5z9A0Bq1Cw2Dv3Eu4Ft5Gs8=',
        'challenge' => '1763116800.q8Jx2v3mQ0yS6nB1kR9tLw.Wm9vYmFyYmF6cXV4',
        'attestation' => 'o2NmbXRvYXBwbGUtYXBwYXR0ZXN0…',
    ]),
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

if ($status >= 400) {
    throw new RuntimeException("HTTP $status: $response");
}
print_r(json_decode($response, true));
```

Python:

```python
import requests

res = requests.post(
    "https://api.switchfile.io/v1/app/guest",
    json={
        "key_id": "kWQ0m6p1y4s3Vb2n8c7x5z9A0Bq1Cw2Dv3Eu4Ft5Gs8=",
        "challenge": "1763116800.q8Jx2v3mQ0yS6nB1kR9tLw.Wm9vYmFyYmF6cXV4",
        "attestation": "o2NmbXRvYXBwbGUtYXBwYXR0ZXN0…",
    },
)
res.raise_for_status()
print(res.json())
```

## Request body

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `assertion` | string | no | Assertion of a key that was attested before (base64) (1 to 6000 characters) |
| `attestation` | string | no | Attestation object for a new key (base64). Send it once per key. (1 to 22000 characters) |
| `challenge` | string | yes | (1 to 200 characters) |
| `key_id` | string | yes | Key identifier from DCAppAttestService (base64) (40 to 64 characters) |

## Responses

### 200: OK

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `access_token` | string | yes | Bearer token for conversions without an account. The free files of the day count per device. |
| `expires_in` | string (integer) \| integer | yes | Seconds until the token expires. Ask for a new one with an assertion. |
| `token_type` | string | yes | (one of "Bearer") |

Example (application/json):

```json
{
  "access_token": "sfgt_6f1c0b2e-8d4a-4c7b-9e3f-2a1d5c8b7e60.1763721600.c2lnbmF0dXJl",
  "token_type": "Bearer",
  "expires_in": 604800
}
```

### 400: The challenge has expired or was not issued by us (`INVALID_CHALLENGE`)

Example (application/problem+json):

```json
{
  "type": "https://switchfile.io/docs/api/v1#errors-invalid-challenge",
  "title": "Invalid challenge",
  "status": 400,
  "code": "INVALID_CHALLENGE",
  "detail": "The challenge has expired. Request a new one."
}
```

### 401: App Attest could not verify the device, or the key is not registered (`ATTESTATION_FAILED`)

Example (application/problem+json):

```json
{
  "type": "https://switchfile.io/docs/api/v1#errors-attestation-failed",
  "title": "Attestation failed",
  "status": 401,
  "code": "ATTESTATION_FAILED",
  "detail": "We couldn't verify this device. Try again or sign in."
}
```

### 403: This device can only use the app with an account (`DEVICE_BLOCKED`)

Example (application/problem+json):

```json
{
  "type": "https://switchfile.io/docs/api/v1#errors-device-blocked",
  "title": "Device blocked",
  "status": 403,
  "code": "DEVICE_BLOCKED",
  "detail": "This device can't use the app without an account. Sign in to continue."
}
```

### 409: The key was attested before; send an assertion (`DEVICE_ALREADY_ATTESTED`)

Example (application/problem+json):

```json
{
  "type": "https://switchfile.io/docs/api/v1#errors-device-already-attested",
  "title": "Device already attested",
  "status": 409,
  "code": "DEVICE_ALREADY_ATTESTED",
  "detail": "This key is already registered. Send an assertion instead."
}
```

### 422: Invalid data. errors names the fields (`INVALID_DATA`)

Example (application/problem+json):

```json
{
  "type": "https://switchfile.io/docs/api/v1#errors-invalid-data",
  "title": "Invalid data",
  "status": 422,
  "code": "INVALID_DATA",
  "detail": "The request contains invalid data.",
  "errors": {
    "to": [
      "Expected string"
    ]
  }
}
```

### 429: Too many requests, or too many new devices from one network on one day (`TOO_MANY_DEVICES`)

Example (application/problem+json):

```json
{
  "type": "https://switchfile.io/docs/api/v1#errors-too-many-devices",
  "title": "Too many devices",
  "status": 429,
  "code": "TOO_MANY_DEVICES",
  "detail": "Too many new devices from this network today. Sign in to continue."
}
```

### 503: Not available right now or busy. See Retry-After (`GUEST_UNAVAILABLE`)

Example (application/problem+json):

```json
{
  "type": "https://switchfile.io/docs/api/v1#errors-guest-unavailable",
  "title": "Guest unavailable",
  "status": 503,
  "code": "GUEST_UNAVAILABLE",
  "detail": "Using the app without an account isn't available yet. Sign in to continue."
}
```

Error bodies and codes are explained in [Errors](https://switchfile.io/docs/errors).
