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

# Authentication

> Authenticate API requests using your Project Token

All API requests require a Project Token. The token identifies your organization and authorizes requests to identify users and track events.

## Getting your Project Token

1. Go to [Settings](https://retain.so/dashboard/settings?tab=project-token) and copy your Project Token (it starts with `rk-`)

<Warning>
  Although the Project Token is write-only, we recommend using it only on the
  backend.
</Warning>

## Using the Project Token

In **Node.js**, pass the token when creating the Retain client and the SDK handles authentication automatically.

In **other languages**, include the token in each request using Authorization: `Basic <token>` with `Content-Type: application/json`.

<CodeGroup>
  ```typescript Node.js icon="square-js" focus={3} theme={"theme":"vesper"}
  import { Retain } from '@retain-so/sdk';

  const retain = new Retain(process.env.RETAIN_WRITE_KEY);

  ```

  ```python Python icon=python focus={6-9} theme={"theme":"vesper"}
  import os
  import requests

  requests.post(
      'https://api.retain.so/identify',
      headers={
          'Content-Type': 'application/json',
          'Authorization': f'Basic {os.environ["RETAIN_WRITE_KEY"]}',
      },
      json={
          'userId': 'user_123',
          'email': 'jane@example.com',
      },
  )
  ```

  ```php PHP icon=php focus={5-8} theme={"theme":"vesper"}
  $ch = curl_init('https://api.retain.so/identify');
  curl_setopt_array($ch, [
      CURLOPT_POST => true,
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_HTTPHEADER => [
          'Content-Type: application/json',
          'Authorization: Basic ' . getenv('RETAIN_WRITE_KEY'),
      ],
      CURLOPT_POSTFIELDS => json_encode([
          'userId' => 'user_123',
          'email' => 'jane@example.com',
      ]),
  ]);
  curl_exec($ch);
  curl_close($ch);
  ```

  ```ruby Ruby icon=gem focus={6-7} theme={"theme":"vesper"}
  require 'net/http'
  require 'json'

  uri = URI('https://api.retain.so/identify')
  req = Net::HTTP::Post.new(uri)
  req['Content-Type'] = 'application/json'
  req['Authorization'] = "Basic #{ENV.fetch('RETAIN_WRITE_KEY')}"
  req.body = { userId: 'user_123', email: 'jane@example.com' }.to_json

  Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  ```

  ```java Java icon=java focus={12-13} theme={"theme":"vesper"}
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;

  String json = """
    {"userId":"user_123","email":"jane@example.com"}
    """;

  HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://api.retain.so/identify"))
      .header("Content-Type", "application/json")
      .header("Authorization", "Basic " + System.getenv("RETAIN_WRITE_KEY"))
      .POST(HttpRequest.BodyPublishers.ofString(json))
      .build();

  HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
  ```

  ```bash cURL focus={2-3} theme={"theme":"vesper"}
  curl -X POST https://api.retain.so/identify \
    -H "Content-Type: application/json" \
    -H "Authorization: Basic $RETAIN_WRITE_KEY" \
    -d '{
      "userId": "user_123",
      "email": "jane@example.com"
    }'
  ```
</CodeGroup>

See [Identify](/docs/guides/identify) and [Track](/docs/guides/track) for the same pattern with full request bodies in each language.

## Next steps

<CardGroup cols={2}>
  <Card title="Identify" icon="user" href="/docs/guides/identify">
    Identify users in Retain.
  </Card>

  <Card title="Track" icon="chart-line" href="/docs/guides/track">
    Record events for users.
  </Card>
</CardGroup>
