Authentication & Setup

Written By Stanislas

Last updated 16 days ago


Authenticate your users with the Swiftask Public Bot API using a simple REST endpoint, then configure your GraphQL client for subsequent API calls.

Overview

The Swiftask authentication process is a two-step flow: first, you exchange your client token and a user identifier (client UUID) for an access token via REST; second, you configure your GraphQL client with that token to make authenticated API requests.

This guide covers how to implement authentication, store credentials securely, and set up your GraphQL and WebSocket clients for real-time features.


Prerequisites

Before you authenticate, ensure you have:

  1. Client Token β€” A unique token for your agent (obtained from your Swiftask workspace Developer section)

  2. Client UUID β€” A unique identifier for each user or session (generated client-side)

  3. GraphQL client library β€” Apollo Client or a similar GraphQL client (already installed)

Obtaining your client token

  1. Log in to your Swiftask workspace

  2. Navigate to your agent's Developer section

  3. Find the API Access area and copy your clientToken

  4. Keep this token secure; it identifies your agent to the API


Getting started

Here's the minimal setup to authenticate and create a GraphQL client:

Step 1: Generate a client UUID

The client UUID uniquely identifies a user or session on the client side. Generate it once and store it for persistence.

// Method 1: Using the browser Crypto API (recommended)
const clientUuid = crypto.randomUUID();
localStorage.setItem('swiftask_client_uuid', clientUuid);

// Method 2: Using the uuid library (if installed)
import { v4 as uuidv4 } from 'uuid';
const clientUuid = uuidv4();
localStorage.setItem('swiftask_client_uuid', clientUuid);

// Retrieve on subsequent visits
const storedUuid = localStorage.getItem('swiftask_client_uuid') || crypto.randomUUID();

Step 2: Call the REST authentication endpoint

Exchange your client token and UUID for an access token.

const clientToken = 'your_agent_client_token';
const clientUuid = localStorage.getItem('swiftask_client_uuid') || crypto.randomUUID();

const response = await fetch(`https://graphql.swiftask.ai/public/widget-bot/${clientToken}`, {
  method: 'GET',
  headers: {
    'x-client-uuid': clientUuid,
    'Content-Type': 'application/json',
  },
});

const result = await response.json();

if (result.success) {
  const { accessToken, botSlug, todoId, workspaceId, starterSessionId } = result.data;
  console.log('Authentication successful');
} else {
  console.error('Authentication failed:', result.error);
}

Step 3: Configure your GraphQL client

Use the accessToken and workspaceId from the authentication response to set up Apollo Client.

import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';

const httpLink = createHttpLink({
  uri: 'https://graphql.swiftask.ai/graphql',
});

const authLink = setContext((_, { headers }) => {
  return {
    headers: {
      ...headers,
      authorization: `Bearer ${accessToken}`,
      'x-workspace-id': workspaceId, // Required for request routing
      'x-client': 'widget', // Identifies this as a public widget client
    },
  };
});

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
});

Understanding the authentication response

The REST endpoint returns a configuration object with everything you need to proceed:

Field

Type

Purpose

accessToken

string

JWT token for authenticating GraphQL requests

botSlug

string

Unique identifier for your agent

todoId

number

Container ID for this user's chat sessions

workspaceId

number

Your agent's workspace ID (required for GraphQL headers)

starterSessionId

number

A default chat session, ready to use immediately

clientUuid

string

Echo of the client UUID you sent

widgetAppearance

object

Appearance configuration for your chat widget

Store these values; you'll use them for subsequent API calls.

Setting up WebSocket for real-time features

If you plan to use subscriptions (real-time message streaming, tool call events, etc.), configure a WebSocket connection alongside your HTTP GraphQL client.

import { WebSocketLink } from '@apollo/client/link/ws';
import { split } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';

const wsLink = new WebSocketLink({
  uri: 'wss://graphql.swiftask.ai/graphql',
  options: {
    reconnect: true,
    connectionParams: {
      authorization: `Bearer ${accessToken}`,
      workspaceId: workspaceId, // Required for WebSocket routing
    },
  },
});

// Split: queries/mutations use HTTP, subscriptions use WebSocket
const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return definition.kind === 'OperationDefinition' && definition.operation === 'subscription';
  },
  wsLink,
  authLink.concat(httpLink)
);

const client = new ApolloClient({
  link: splitLink,
  cache: new InMemoryCache(),
});

Complete authentication class

Here's a reusable class that handles the full authentication flow and client setup:

class SwiftaskAuthClient {
  constructor(clientToken, apiUrl = 'https://graphql.swiftask.ai') {
    this.clientToken = clientToken;
    this.apiUrl = apiUrl;
    this.clientUuid = this.getOrCreateClientUuid();
    this.config = null;
    this.client = null;
  }

  getOrCreateClientUuid() {
    let uuid = localStorage.getItem('swiftask_client_uuid');
    if (!uuid) {
      uuid = crypto.randomUUID();
      localStorage.setItem('swiftask_client_uuid', uuid);
    }
    return uuid;
  }

  async authenticate() {
    const response = await fetch(
      `${this.apiUrl}/public/widget-bot/${this.clientToken}`,
      {
        method: 'GET',
        headers: {
          'x-client-uuid': this.clientUuid,
          'Content-Type': 'application/json',
        },
      }
    );

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error?.message || 'Authentication failed');
    }

    const result = await response.json();

    if (!result.success) {
      throw new Error('Authentication failed');
    }

    this.config = result.data;
    return this.config;
  }

  setupGraphQLClient() {
    if (!this.config) {
      throw new Error('Not authenticated. Call authenticate() first.');
    }

    const httpLink = createHttpLink({
      uri: `${this.apiUrl}/graphql`,
    });

    const authLink = setContext((_, { headers }) => ({
      headers: {
        ...headers,
        authorization: `Bearer ${this.config.accessToken}`,
        'x-workspace-id': this.config.workspaceId,
        'x-client': 'widget',
      },
    }));

    const wsLink = new WebSocketLink({
      uri: `wss://graphql.swiftask.ai/graphql`,
      options: {
        reconnect: true,
        connectionParams: {
          authorization: `Bearer ${this.config.accessToken}`,
          workspaceId: this.config.workspaceId,
        },
      },
    });

    const splitLink = split(
      ({ query }) => {
        const definition = getMainDefinition(query);
        return definition.kind === 'OperationDefinition' && definition.operation === 'subscription';
      },
      wsLink,
      authLink.concat(httpLink)
    );

    this.client = new ApolloClient({
      link: splitLink,
      cache: new InMemoryCache(),
    });

    return this.client;
  }

  getClient() {
    if (!this.client) {
      throw new Error('GraphQL client not initialized. Call setupGraphQLClient() first.');
    }
    return this.client;
  }

  getConfig() {
    return this.config;
  }
}

// Usage
const authClient = new SwiftaskAuthClient('your_client_token');
await authClient.authenticate();
const client = authClient.setupGraphQLClient();

Handling authentication errors

The REST endpoint may return errors. Handle them gracefully:

HTTP Status

Error Code

Meaning

Action

400

Bad Request

Missing or invalid parameters

Check your clientToken and x-client-uuid header

401

Unauthorized

Invalid or expired token

Verify your clientToken is correct

403

Forbidden

Agent is not public

Enable public access in your agent settings

404

Not Found

Agent does not exist

Confirm the agent exists in your workspace

500

Internal Server Error

Server-side issue

Retry after a short delay

const authenticateWithErrorHandling = async (clientToken, clientUuid) => {
  try {
    const response = await fetch(
      `https://graphql.swiftask.ai/public/widget-bot/${clientToken}`,
      {
        headers: { 'x-client-uuid': clientUuid },
      }
    );

    if (!response.ok) {
      const error = await response.json();

      switch (response.status) {
        case 400:
          throw new Error('Invalid request: Check your parameters');
        case 401:
          throw new Error('Authentication failed: Invalid client token');
        case 403:
          throw new Error('Access denied: Agent may not be public');
        case 404:
          throw new Error('Agent not found');
        case 500:
          throw new Error('Server error: Try again later');
        default:
          throw new Error(`Unknown error: ${response.status}`);
      }
    }

    return await response.json();
  } catch (error) {
    if (error instanceof TypeError) {
      throw new Error('Network error: Check your connection');
    }
    throw error;
  }
};

Best practices

  1. Store the client UUID persistently β€” Always save it to localStorage or equivalent storage so the same user is recognized across sessions.

  2. Keep your client token secure β€” Never expose it in client-side code repositories. Use environment variables or secure backend proxies in production.

  3. Treat the access token as sensitive β€” Store it securely and never log it to the console in production.

  4. Implement token refresh logic β€” Tokens may expire. Check the token's expiration time and refresh when needed.

  5. Use HTTPS/WSS in production β€” Always use secure connections for authentication and real-time features.

  6. Handle network errors gracefully β€” Implement retry logic with exponential backoff for transient failures.

  7. Validate the response structure β€” Always check that the authentication response contains expected fields before using them.