> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pontisglobe.com/llms.txt
> Use this file to discover all available pages before exploring further.

# POST /api/v1/user/login

> Exchange your account credentials for a 15-minute JWT.

```
POST /api/v1/user/login
```

## Headers

| Header         | Required | Notes                                                      |
| -------------- | -------- | ---------------------------------------------------------- |
| `x-api-key`    | yes      | Your API key                                               |
| `x-timestamp`  | yes      | Unix seconds, current ±5 min                               |
| `x-signature`  | yes      | HMAC-SHA256(`${timestamp}.${encrypted_body}`, HMAC secret) |
| `content-type` | yes      | `application/json`                                         |

## Request body

```json theme={null}
{ "data": "<aes-256-gcm encrypted blob of the payload below>" }
```

Decrypted payload:

| Field      | Type   | Required | Notes                                   |
| ---------- | ------ | -------- | --------------------------------------- |
| `email`    | string | yes      | The owner email on the consumer account |
| `password` | string | yes      | The owner's password                    |

## Example request

<Info>
  The snippets below assume you've already encrypted the body and computed the signature — see the [Quickstart](/quickstart) for the full helper in Node and Python.
</Info>

<CodeGroup>
  ```js Node theme={null}
  const login = await call('/api/v1/user/login', {
    email: 'you@example.com',
    password: 'YOUR_PASSWORD',
  })
  ```

  ```python Python theme={null}
  status, body = call('/api/v1/user/login', {
      'email': 'you@example.com',
      'password': 'YOUR_PASSWORD',
  })
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.pontisglobe.com/api/v1/user/login" \
    -H "content-type: application/json" \
    -H "x-api-key: YOUR_API_KEY" \
    -H "x-timestamp: 1748023400" \
    -H "x-signature: 2f8a9b…" \
    -d '{"data":"<encrypted blob>"}'
  ```

  ```go Go theme={null}
  req, _ := http.NewRequest("POST",
    "https://api.pontisglobe.com/api/v1/user/login",
    strings.NewReader(`{"data":"`+encryptedBody+`"}`))
  req.Header.Set("content-type", "application/json")
  req.Header.Set("x-api-key", apiKey)
  req.Header.Set("x-timestamp", timestamp)
  req.Header.Set("x-signature", signature)
  res, _ := http.DefaultClient.Do(req)
  ```

  ```java Java theme={null}
  HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.pontisglobe.com/api/v1/user/login"))
    .header("content-type", "application/json")
    .header("x-api-key", apiKey)
    .header("x-timestamp", timestamp)
    .header("x-signature", signature)
    .POST(HttpRequest.BodyPublishers.ofString("{\"data\":\"" + encryptedBody + "\"}"))
    .build();
  HttpResponse<String> res = HttpClient.newHttpClient().send(req, BodyHandlers.ofString());
  ```

  ```kotlin Kotlin theme={null}
  val req = Request.Builder()
    .url("https://api.pontisglobe.com/api/v1/user/login")
    .post("""{"data":"$encryptedBody"}""".toRequestBody("application/json".toMediaType()))
    .addHeader("x-api-key", apiKey)
    .addHeader("x-timestamp", timestamp)
    .addHeader("x-signature", signature)
    .build()
  val res = OkHttpClient().newCall(req).execute()
  ```

  ```php PHP theme={null}
  $ch = curl_init('https://api.pontisglobe.com/api/v1/user/login');
  curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
      'content-type: application/json',
      "x-api-key: $apiKey",
      "x-timestamp: $timestamp",
      "x-signature: $signature",
    ],
    CURLOPT_POSTFIELDS => json_encode(['data' => $encryptedBody]),
    CURLOPT_RETURNTRANSFER => true,
  ]);
  $res = curl_exec($ch);
  ```

  ```ruby Ruby theme={null}
  uri = URI('https://api.pontisglobe.com/api/v1/user/login')
  req = Net::HTTP::Post.new(uri, {
    'content-type' => 'application/json',
    'x-api-key' => api_key,
    'x-timestamp' => timestamp,
    'x-signature' => signature,
  })
  req.body = { data: encrypted_body }.to_json
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  ```

  ```csharp C# theme={null}
  var req = new HttpRequestMessage(HttpMethod.Post,
    "https://api.pontisglobe.com/api/v1/user/login");
  req.Headers.Add("x-api-key", apiKey);
  req.Headers.Add("x-timestamp", timestamp);
  req.Headers.Add("x-signature", signature);
  req.Content = new StringContent($"{{\"data\":\"{encryptedBody}\"}}",
    Encoding.UTF8, "application/json");
  var res = await new HttpClient().SendAsync(req);
  ```

  ```swift Swift theme={null}
  var req = URLRequest(url: URL(string: "https://api.pontisglobe.com/api/v1/user/login")!)
  req.httpMethod = "POST"
  req.setValue("application/json", forHTTPHeaderField: "content-type")
  req.setValue(apiKey, forHTTPHeaderField: "x-api-key")
  req.setValue(timestamp, forHTTPHeaderField: "x-timestamp")
  req.setValue(signature, forHTTPHeaderField: "x-signature")
  req.httpBody = #"{"data":"\#(encryptedBody)"}"#.data(using: .utf8)
  let (data, _) = try await URLSession.shared.data(for: req)
  ```
</CodeGroup>

## Responses

### 200 — Success

```json theme={null}
{
  "ok": true,
  "data": {
    "access_token": "eyJhbGciOi...",
    "token_type": "Bearer",
    "expires_in": 900
  }
}
```

Pass `access_token` as `Authorization: Bearer <token>` on subsequent requests.

### 401 — unauthorized

Wrong email/password, or account suspended.

```json theme={null}
{
  "ok": false,
  "request_id": "01J9F5Z4X7Y3K2B1Q0M8N7V6T5",
  "error": {
    "code": "unauthorized",
    "message": "Invalid credentials. Make sure the email, password, and API key all belong to the same user account."
  }
}
```

### 429 — rate\_limited

Too many login attempts (scoped per consumer + mode).

```json theme={null}
{
  "ok": false,
  "request_id": "01J9F5Z4X7Y3K2B1Q0M8N7V6T5",
  "error": { "code": "rate_limited", "message": "Too many login attempts. Try again later." }
}
```

### 500 — internal\_error

Account misconfiguration — contact support with `request_id`.

See [Errors](/errors) for the full envelope shape and full code list.
