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

# Getting Started Guide

> Walk through integrating the Urtentic API into your application step by step, with practical examples and code samples.

## Prerequisites

Before you begin, you'll need:

1. **Urtentic Account**: Sign up at [urtentic.com](https://app.urtentic.com)
2. **API Credentials**: Obtain your Client ID and API Secret from the integrations page in your dashboard
3. **Workflow ID**: Create a verification workflow in your dashboard

### Generate Your API Credentials

<Frame>
  <img src="https://mintcdn.com/urtentic/O2Vtp_Kp7cQUQUvy/images/screenshots/api-key-generation.png?fit=max&auto=format&n=O2Vtp_Kp7cQUQUvy&q=85&s=fd76dc1382cf5b38079ab434d39ae550" alt="API Key Generation" width="2638" height="555" data-path="images/screenshots/api-key-generation.png" />
</Frame>

Navigate to the integrations page in your dashboard to obtain your API credentials:

* **Client ID**: Your application identifier (sent via `X-CLIENT-ID` header)
* **Client Secret**: Your authentication token (sent via `Authorization: Bearer` header)

### Create a Workflow

<Frame>
  <img src="https://mintcdn.com/urtentic/O2Vtp_Kp7cQUQUvy/images/screenshots/workflow-creation.png?fit=max&auto=format&n=O2Vtp_Kp7cQUQUvy&q=85&s=244461c72543be42e71415616ae6b015" alt="Workflow Creation" width="2644" height="954" data-path="images/screenshots/workflow-creation.png" />
</Frame>

Set up your verification workflow by defining the required verification processes.

<Frame>
  <img src="https://mintcdn.com/urtentic/O2Vtp_Kp7cQUQUvy/images/screenshots/workflow-configuration.png?fit=max&auto=format&n=O2Vtp_Kp7cQUQUvy&q=85&s=63148baea8c90f48a828404905b29d38" alt="Workflow Configuration" width="2641" height="1109" data-path="images/screenshots/workflow-configuration.png" />
</Frame>

Configure your workflow settings including document types, verification steps, and security options.

## Quick Start (5 minutes)

### Step 1: Set Up Your Environment

First, set your API credentials as environment variables:

```bash theme={null}
export URTENTIC_API_KEY="your-base64-encoded-api-key"
export URTENTIC_CLIENT_ID="your-base64-encoded-client-id"
```

### Step 2: Create Your First Verification

```bash theme={null}
curl -X POST https://api.urtentic.com/api/v1/verifications \
  -H "Authorization: Bearer $URTENTIC_API_KEY" \
  -H "X-CLIENT-ID: $URTENTIC_CLIENT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "flowId": "db50ed08-18fa-41e8-9e46-a44aca39f69d",
    "metadata": {
      "userId": "user_12345",
      "applicationId": "app_67890"
    }
  }'
```

**Response:**

```json theme={null}
{
  "id": "85c94e71-6e3f-4a19-b15c-781d8a876542",
  "flowId": "db50ed08-18fa-41e8-9e46-a44aca39f69d",
  "status": "pending",
  "metadata": {
    "userId": "user_12345",
    "applicationId": "app_67890"
  },
  "createdAt": "2023-12-01T10:00:00Z",
  "verificationUrl": "https://verify.urtentic.com?flowId=db50ed08-18fa-41e8-9e46-a44aca39f69d&clientId=YOUR_CLIENT_ID&metadata=%7B%22userId%22%3A%22user_12345%22%7D"
}
```

### Step 3: Check Verification Status

```bash theme={null}
curl -X GET https://api.urtentic.com/api/v1/verifications/85c94e71-6e3f-4a19-b15c-781d8a876542 \
  -H "Authorization: Bearer $URTENTIC_API_KEY" \
  -H "X-CLIENT-ID: $URTENTIC_CLIENT_ID"
```

## Integration Examples

<CodeGroup>
  ```javascript Node.js theme={null}
  const URTENTIC_API_KEY = process.env.URTENTIC_API_KEY;
  const URTENTIC_CLIENT_ID = process.env.URTENTIC_CLIENT_ID;
  const BASE_URL = 'https://api.urtentic.com/api/v1';

  // Create a verification
  async function createVerification(flowId, metadata = {}) {
    const response = await fetch(`${BASE_URL}/verifications`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${URTENTIC_API_KEY}`,
        'X-CLIENT-ID': URTENTIC_CLIENT_ID,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ flowId, metadata })
    });
    return await response.json();
  }

  // Check verification status
  async function getVerificationStatus(verificationId) {
    const response = await fetch(`${BASE_URL}/verifications/${verificationId}`, {
      headers: {
        'Authorization': `Bearer ${URTENTIC_API_KEY}`,
        'X-CLIENT-ID': URTENTIC_CLIENT_ID
      }
    });
    return await response.json();
  }

  // Usage
  const verification = await createVerification('db50ed08-18fa-41e8-9e46-a44aca39f69d', { userId: 'user_12345' });
  console.log('Verification URL:', verification.verificationUrl);
  ```

  ```python Python theme={null}
  import requests
  import os

  URTENTIC_API_KEY = os.getenv('URTENTIC_API_KEY')
  URTENTIC_CLIENT_ID = os.getenv('URTENTIC_CLIENT_ID')
  BASE_URL = 'https://api.urtentic.com/api/v1'

  def create_verification(flow_id, metadata=None):
      if metadata is None:
          metadata = {}
      response = requests.post(
          f'{BASE_URL}/verifications',
          headers={
              'Authorization': f'Bearer {URTENTIC_API_KEY}',
              'X-CLIENT-ID': URTENTIC_CLIENT_ID,
              'Content-Type': 'application/json'
          },
          json={ 'flowId': flow_id, 'metadata': metadata }
      )
      return response.json()

  def get_verification_status(verification_id):
      response = requests.get(
          f'{BASE_URL}/verifications/{verification_id}',
          headers={
              'Authorization': f'Bearer {URTENTIC_API_KEY}',
              'X-CLIENT-ID': URTENTIC_CLIENT_ID
          }
      )
      return response.json()

  # Usage
  verification = create_verification('db50ed08-18fa-41e8-9e46-a44aca39f69d', { 'userId': 'user_12345' })
  print(f"Verification URL: {verification['verificationUrl']}")
  ```

  ```php PHP theme={null}
  <?php
  $apiKey = getenv('URTENTIC_API_KEY');
  $clientId = getenv('URTENTIC_CLIENT_ID');
  $baseUrl = 'https://api.urtentic.com/api/v1';

  function createVerification($flowId, $metadata = []) {
      global $apiKey, $clientId, $baseUrl;
      $data = [ 'flowId' => $flowId, 'metadata' => $metadata ];
      $context = stream_context_create([
          'http' => [
              'method' => 'POST',
              'header' => [
                  "Authorization: Bearer $apiKey",
                  "X-CLIENT-ID: $clientId",
                  'Content-Type: application/json'
              ],
              'content' => json_encode($data)
          ]
      ]);
      $response = file_get_contents("$baseUrl/verifications", false, $context);
      return json_decode($response, true);
  }

  $verification = createVerification('db50ed08-18fa-41e8-9e46-a44aca39f69d', [ 'userId' => 'user_12345' ]);
  echo "Verification URL: " . $verification['verificationUrl'] . PHP_EOL;
  ?>
  ```

  ```java Java theme={null}
  import com.fasterxml.jackson.databind.ObjectMapper;
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import java.util.Map;

  public class UrtenticClient {
      private static final String API_KEY = System.getenv("URTENTIC_API_KEY");
      private static final String CLIENT_ID = System.getenv("URTENTIC_CLIENT_ID");
      private static final String BASE_URL = "https://api.urtentic.com/api/v1";

      private final HttpClient httpClient = HttpClient.newHttpClient();
      private final ObjectMapper mapper = new ObjectMapper();

      // Create a verification
      public Map<?, ?> createVerification(String flowId, Map<String, Object> metadata) throws Exception {
          Map<String, Object> body = Map.of("flowId", flowId, "metadata", metadata);
          String json = mapper.writeValueAsString(body);

          HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create(BASE_URL + "/verifications"))
              .header("Authorization", "Bearer " + API_KEY)
              .header("X-CLIENT-ID", CLIENT_ID)
              .header("Content-Type", "application/json")
              .POST(HttpRequest.BodyPublishers.ofString(json))
              .build();

          HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
          return mapper.readValue(response.body(), Map.class);
      }

      // Check verification status
      public Map<?, ?> getVerificationStatus(String verificationId) throws Exception {
          HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create(BASE_URL + "/verifications/" + verificationId))
              .header("Authorization", "Bearer " + API_KEY)
              .header("X-CLIENT-ID", CLIENT_ID)
              .GET()
              .build();

          HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
          return mapper.readValue(response.body(), Map.class);
      }

      // Usage
      public static void main(String[] args) throws Exception {
          UrtenticClient client = new UrtenticClient();
          Map<String, Object> metadata = Map.of("userId", "user_12345");
          Map<?, ?> verification = client.createVerification("db50ed08-18fa-41e8-9e46-a44aca39f69d", metadata);
          System.out.println("Verification URL: " + verification.get("verificationUrl"));
      }
  }
  ```

  ```bash cURL theme={null}
  # Set your API credentials
  export URTENTIC_API_KEY="your-base64-encoded-api-key"
  export URTENTIC_CLIENT_ID="your-base64-encoded-client-id"

  # Create a verification
  curl -X POST https://api.urtentic.com/api/v1/verifications \
    -H "Authorization: Bearer $URTENTIC_API_KEY" \
    -H "X-CLIENT-ID: $URTENTIC_CLIENT_ID" \
    -H "Content-Type: application/json" \
    -d '{
      "flowId": "db50ed08-18fa-41e8-9e46-a44aca39f69d",
      "metadata": {
        "userId": "user_12345",
        "applicationId": "app_67890"
      }
    }'

  # Check verification status
  curl -X GET https://api.urtentic.com/api/v1/verifications/85c94e71-6e3f-4a19-b15c-781d8a876542 \
    -H "Authorization: Bearer $URTENTIC_API_KEY" \
    -H "X-CLIENT-ID: $URTENTIC_CLIENT_ID"
  ```
</CodeGroup>

## Workflow Types

Each workflow is configured in your Urtentic dashboard — the steps it includes (document verification, liveness, email, etc.) are defined there, not in the API request. The `flowId` identifies which workflow to run, and `metadata` carries your own tracking data.

### Document Verification Only

```json theme={null}
{
  "flowId": "db50ed08-18fa-41e8-9e46-a44aca39f69d",
  "metadata": {
    "userId": "user_12345"
  }
}
```

### Liveness + Document Verification

```json theme={null}
{
  "flowId": "c3a2b1d0-5e4f-4a19-b15c-781d8a876542",
  "metadata": {
    "userId": "user_12345"
  }
}
```

### Location + Email Verification

```json theme={null}
{
  "flowId": "f7e6d5c4-3b2a-4a19-b15c-781d8a876542",
  "metadata": {
    "userId": "user_12345"
  }
}
```

## Handling Webhooks

Configure webhook endpoints in your dashboard to receive real-time updates:

<CodeGroup>
  ```javascript Node.js theme={null}
  // Express.js webhook handler
  app.post('/webhook/urtentic', express.raw({type: 'application/json'}), (req, res) => {
    const signature = req.headers['x-urtentic-signature'];
    const timestamp = req.headers['x-urtentic-timestamp'];
    const payload = req.body;

    try {
      const currentTime = Math.floor(Date.now() / 1000);
      if (Math.abs(currentTime - parseInt(timestamp)) > 300) {
        return res.status(400).send('Webhook timestamp too old');
      }

      const secretBytes = Buffer.from(WEBHOOK_SECRET, 'base64');
      const expectedSignature = crypto
        .createHmac('sha256', secretBytes)
        .update(payload)
        .digest('hex');

      let sig = signature.startsWith('sha256=') ? signature.substring(7) : signature;

      if (sig !== expectedSignature) {
        return res.status(400).send('Invalid signature');
      }
    } catch (error) {
      return res.status(400).send('Signature verification failed');
    }

    const event = JSON.parse(payload);

    switch (event.eventName) {
      case 'verification_completed':
        console.log('Verification completed:', event.flowId);
        break;
      case 'verification_abandoned':
        console.log('Verification abandoned:', event.flowId);
        break;
      case 'step_completed':
        console.log('Step completed:', event.flowId, 'Step:', event.id);
        break;
    }

    res.status(200).send('OK');
  });
  ```

  ```python Python theme={null}
  from flask import Flask, request, jsonify
  import hmac, hashlib, json, os, base64, time

  app = Flask(__name__)
  WEBHOOK_SECRET = os.getenv('WEBHOOK_SECRET')

  @app.route('/webhook/urtentic', methods=['POST'])
  def handle_webhook():
      signature = request.headers.get('x-urtentic-signature')
      timestamp = request.headers.get('x-urtentic-timestamp')
      payload = request.get_data()

      try:
          current_time = int(time.time())
          if abs(current_time - int(timestamp)) > 300:
              return jsonify({'error': 'Webhook timestamp too old'}), 400

          secret_bytes = base64.b64decode(WEBHOOK_SECRET)
          expected_signature = hmac.new(
              secret_bytes, payload, hashlib.sha256
          ).hexdigest()

          sig = signature[7:] if signature.startswith('sha256=') else signature
          if sig != expected_signature:
              return jsonify({'error': 'Invalid signature'}), 400
      except Exception:
          return jsonify({'error': 'Signature verification failed'}), 400

      event = json.loads(payload)

      if event['eventName'] == 'verification_completed':
          print(f"Verification completed: {event['flowId']}")
      elif event['eventName'] == 'step_completed':
          print(f"Step completed: {event['flowId']}")

      return jsonify({'status': 'ok'})
  ```

  ```php PHP theme={null}
  <?php
  $webhookSecret = getenv('WEBHOOK_SECRET');
  $payload = file_get_contents('php://input');
  $signature = $_SERVER['HTTP_X_URTENTIC_SIGNATURE'] ?? '';
  $timestamp = $_SERVER['HTTP_X_URTENTIC_TIMESTAMP'] ?? '';

  try {
      $currentTime = time();
      if (abs($currentTime - intval($timestamp)) > 300) {
          http_response_code(400);
          echo json_encode(['error' => 'Webhook timestamp too old']);
          exit;
      }

      $secretBytes = base64_decode($webhookSecret);
      $expectedSignature = hash_hmac('sha256', $payload, $secretBytes);
      $sig = (strpos($signature, 'sha256=') === 0) ? substr($signature, 7) : $signature;

      if (!hash_equals($expectedSignature, $sig)) {
          http_response_code(400);
          echo json_encode(['error' => 'Invalid signature']);
          exit;
      }
  } catch (Exception $e) {
      http_response_code(400);
      echo json_encode(['error' => 'Signature verification failed']);
      exit;
  }

  $event = json_decode($payload, true);
  switch ($event['eventName']) {
      case 'verification_completed':
          error_log('Verification completed: ' . $event['flowId']);
          break;
      case 'step_completed':
          error_log('Step completed: ' . $event['flowId']);
          break;
  }

  http_response_code(200);
  echo json_encode(['status' => 'ok']);
  ?>
  ```

  ```java Java theme={null}
  import com.fasterxml.jackson.databind.ObjectMapper;
  import com.sun.net.httpserver.HttpExchange;
  import com.sun.net.httpserver.HttpHandler;
  import javax.crypto.Mac;
  import javax.crypto.spec.SecretKeySpec;
  import java.io.InputStream;
  import java.util.Base64;
  import java.util.Map;

  public class UrtenticWebhookHandler implements HttpHandler {
      private static final String WEBHOOK_SECRET = System.getenv("WEBHOOK_SECRET");
      private final ObjectMapper mapper = new ObjectMapper();

      @Override
      public void handle(HttpExchange exchange) throws IOException {
          String signature = exchange.getRequestHeaders().getFirst("x-urtentic-signature");
          String timestamp  = exchange.getRequestHeaders().getFirst("x-urtentic-timestamp");
          byte[] payload    = exchange.getRequestBody().readAllBytes();

          try {
              long currentTime = System.currentTimeMillis() / 1000;
              if (Math.abs(currentTime - Long.parseLong(timestamp)) > 300) {
                  sendResponse(exchange, 400, "Webhook timestamp too old");
                  return;
              }

              byte[] secretBytes = Base64.getDecoder().decode(WEBHOOK_SECRET);
              Mac mac = Mac.getInstance("HmacSHA256");
              mac.init(new SecretKeySpec(secretBytes, "HmacSHA256"));
              String expected = bytesToHex(mac.doFinal(payload));
              String incoming = signature.startsWith("sha256=") ? signature.substring(7) : signature;

              if (!expected.equals(incoming)) {
                  sendResponse(exchange, 400, "Invalid signature");
                  return;
              }
          } catch (Exception e) {
              sendResponse(exchange, 400, "Signature verification failed");
              return;
          }

          Map<?, ?> event = mapper.readValue(payload, Map.class);
          String eventName = (String) event.get("eventName");

          switch (eventName) {
              case "verification_completed" ->
                  System.out.println("Verification completed: " + event.get("flowId"));
              case "verification_abandoned" ->
                  System.out.println("Verification abandoned: " + event.get("flowId"));
              case "step_completed" ->
                  System.out.println("Step completed: " + event.get("flowId") + " Step: " + event.get("id"));
          }

          sendResponse(exchange, 200, "OK");
      }

      private void sendResponse(HttpExchange exchange, int status, String body) throws IOException {
          byte[] bytes = body.getBytes();
          exchange.sendResponseHeaders(status, bytes.length);
          exchange.getResponseBody().write(bytes);
          exchange.getResponseBody().close();
      }

      private String bytesToHex(byte[] bytes) {
          StringBuilder sb = new StringBuilder();
          for (byte b : bytes) sb.append(String.format("%02x", b));
          return sb.toString();
      }
  }
  ```
</CodeGroup>

## Error Handling

The API uses standard HTTP status codes and returns detailed error messages:

<CodeGroup>
  ```javascript Node.js theme={null}
  async function handleApiCall() {
    try {
      const response = await fetch(`${BASE_URL}/verifications`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${URTENTIC_API_KEY}`,
          'X-CLIENT-ID': URTENTIC_CLIENT_ID,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ flowId: 'db50ed08-18fa-41e8-9e46-a44aca39f69d' })
      });

      if (!response.ok) {
        const error = await response.json();
        console.error('API Error:', error);
        switch (response.status) {
          case 400: console.log('Bad request - check your payload'); break;
          case 401: console.log('Unauthorized - check your API credentials'); break;
          case 429: console.log('Rate limited - wait before retrying'); break;
        }
        return null;
      }
      return await response.json();
    } catch (error) {
      console.error('Network error:', error);
      return null;
    }
  }
  ```

  ```python Python theme={null}
  import requests
  from requests.exceptions import RequestException

  def handle_api_call():
      try:
          response = requests.post(
              f'{BASE_URL}/verifications',
              headers={
                  'Authorization': f'Bearer {URTENTIC_API_KEY}',
                  'X-CLIENT-ID': URTENTIC_CLIENT_ID,
                  'Content-Type': 'application/json'
              },
              json={'flowId': 'db50ed08-18fa-41e8-9e46-a44aca39f69d'}
          )
          if not response.ok:
              error_data = response.json()
              print(f'API Error: {error_data}')
              return None
          return response.json()
      except RequestException as e:
          print(f'Network error: {e}')
          return None
  ```

  ```php PHP theme={null}
  <?php
  function handleApiCall() {
      global $apiKey, $clientId, $baseUrl;
      $data = ['flowId' => 'db50ed08-18fa-41e8-9e46-a44aca39f69d'];
      $context = stream_context_create([
          'http' => [
              'method' => 'POST',
              'header' => [
                  "Authorization: Bearer $apiKey",
                  "X-CLIENT-ID: $clientId",
                  'Content-Type: application/json'
              ],
              'content' => json_encode($data),
              'ignore_errors' => true
          ]
      ]);
      $response = file_get_contents("$baseUrl/verifications", false, $context);
      if ($response === false) {
          error_log('Network error occurred');
          return null;
      }
      return json_decode($response, true);
  }
  ?>
  ```

  ```java Java theme={null}
  import com.fasterxml.jackson.databind.ObjectMapper;
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import java.util.Map;

  public Map<?, ?> handleApiCall() {
      try {
          String json = mapper.writeValueAsString(
              Map.of("flowId", "db50ed08-18fa-41e8-9e46-a44aca39f69d")
          );

          HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create(BASE_URL + "/verifications"))
              .header("Authorization", "Bearer " + API_KEY)
              .header("X-CLIENT-ID", CLIENT_ID)
              .header("Content-Type", "application/json")
              .POST(HttpRequest.BodyPublishers.ofString(json))
              .build();

          HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

          if (response.statusCode() >= 400) {
              System.err.println("API Error " + response.statusCode() + ": " + response.body());
              switch (response.statusCode()) {
                  case 400 -> System.err.println("Bad request - check your payload");
                  case 401 -> System.err.println("Unauthorized - check your API credentials");
                  case 429 -> System.err.println("Rate limited - wait before retrying");
              }
              return null;
          }

          return mapper.readValue(response.body(), Map.class);
      } catch (Exception e) {
          System.err.println("Network error: " + e.getMessage());
          return null;
      }
  }
  ```
</CodeGroup>

## Next Steps

* [API Reference](/api-reference/introduction) - Complete API documentation
* [SDK Integration](/sdk/introduction) - Client-side SDKs
* [Webhook Setup](/webhooks/overview) - Real-time notifications

## Support

Need help? We're here to assist:

* **Email**: [support@urtentic.com](mailto:support@urtentic.com)
* **Documentation**: [docs.urtentic.com](https://docs.urtentic.com)
* **Status Page**: [status.urtentic.com](https://status.urtentic.com)
