Code-based API connections allow you to create custom integrations with any external API using JavaScript. This enables you to support any authentication flow or API structure that your external systems require.

You will implement a function that takes a request as input and returns the response that will be sent to the GenerativeAgent. This allows you to transform data, handle complex authentication flows, and integrate with any external system.

Before You Begin

Before you begin, you will need:

  • An ASAPP Dashboard account with Edit permissions for Code based API Connections.

    Your Admin should be able to enable this for you. Reach out to your ASAPP account team if you need help.

  • A basic understanding of JavaScript.

Using Code Based API Connections

1

Create initial Code API Connection

  1. Navigate to API Integration HUB > API Connections
  2. Click Create Connection
  3. Select Code-based API Connection from the dropdown
  4. Enter a Name and optional Description.
  5. Click Create connection

This creates a new, unimplemented API connection. You will be dropped into the Settings tab.

2

Set Up Allowed Domains

Code-based API connections are restricted to an explicit whitelist of domains as a security measure. You need to add each domain that your code will reach out to.

  1. In the Basic Settings tab, specify allowed domains for any API calls your code needs to make
  2. Click Add Domain and enter URLs (e.g., api.example.com)
  3. Wildcards (*) are supported for subdomains
  4. Your code only calls URLs specified in the allowed domains using asappUtilities.callAPI(url, apiRequest)
3

Manage Environment Variables

Store and manage environment variables to be used in your code. This is often used to store data that will change between environments, such as URLs for the API call you will be making.

Environment variables support storing Secret values which are encrypted when stored in the database. However, for API credentials and authentication data, we strongly recommend using Authentication Methods instead, as they provide better security and easier management.

  1. In the Environment Variables settings tab, add any variables your code needs.
  2. Specify the value for Sandbox and Production environments.
  3. Use the Secret checkbox to store encrypted environment variables (for configuration data, not API credentials)
  4. Access environment variables in your code using asappUtilities.getEnvVariable("VARIABLE_NAME")
4

Add Authentication Methods

If your API connection requires authentication, add authentication methods to your API connection.

  1. Navigate to SettingsAuthentication Methods
  2. For each environment, click Add Authentication to add a new authentication method.
  3. Create a new authentication method or select an existing one.
5

Define Request and Response Schemas

You need to define the structure of data that your API connection will receive and return:

  1. Request Schema (schemas/request.json) - Define the request schema that your function will receive
  2. Response Schema (schemas/response.json) - Define the response schema that your function will return

See the Request Schema and Response Schema sections below for detailed information on how to structure these schemas.

6

Implement Your Function

Implement the handleRequest(request) function in src/index.js that takes the request object and returns the response object.

See the Implementing the Handle Function section below for detailed information on how to implement your function.

7

Test Your Code

As you write code, test it to ensure it works as expected:

  1. In the Run panel, define an example request that matches your request.json schema
  2. Select which environment to run: Sandbox or Production
  3. Click Run
  4. Review the results in the left panel

You can not publish your code until you have successfully tested it.

8

Save and Deploy

Once you have successfully tested your API connection:

  1. Publish the API connection. This will save your code and make it available as a new version.

    There is not separate Save vs Publish functionality. You must directly publish any changes to your code.

  2. It will now be available for use in your GenerativeAgent tasks and functions. If you have an existing function that uses this API connection, you will need to update it to use the new version.

  3. Test the integration end-to-end to ensure it works as expected

Request Schema

The request schema (schemas/request.json) defines the structure of data that your function will receive. This JSON schema is both the request schema that your function will receive and the parameters shown to GenerativeAgent.

Add the variables you want as input. Ensure the name and description of each variable is easy for GenerativeAgent to understand.

GenerativeAgent works better with flat JSON object as an input with a reduced number of properties. The more complex the input, the harder it is for GenerativeAgent to understand the request.

By default, the request schema is empty.

{
  "additionalProperties": false,
  "type": "object"
}

Example Request Schema

This example takes a last name and confirmation code as input for an airline rebooking function.

{
  "type": "object",
  "properties": {
    "last_name": {
      "type": "string",
      "description": "The last name of the user"
    },
    "confirmation_code": {
      "type": "string",
      "description": "The flight confirmation code from the user"
    }
  },
  "required": ["last_name", "confirmation_code"]
}

Response Schema

The response schema (schemas/response.json) defines the structure of data that your function must return. This is the response that is shown to GenerativeAgent.

Add the variables you want as output. Ensure the name and description of each variable is easy for GenerativeAgent to understand.

GenerativeAgent can read complex JSON objects more effectively than on request. However, if you are seeing issues with GenerativeAgent not understanding the response, try reducing the number of properties in the response.

By default, the response schema is empty.

{
  "additionalProperties": false,
  "type": "object"
}

Example Response Schema

{
  "type": "object",
  "properties": {
    "response": {
      "type": "string",
      "description": "Human-readable response message"
    },
    "data": {
      "type": "array",
      "description": "Array of search results",
      "items": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "title": { "type": "string" },
          "description": { "type": "string" }
        }
      }
    },
    "metadata": {
      "type": "object",
      "description": "Additional metadata about the response",
      "properties": {
        "totalResults": { "type": "number" },
        "query": { "type": "string" }
      }
    }
  },
  "required": ["response"]
}

Implementing the Handle Function

The handleRequest(request) function in src/index.js is the core of your API connection. This function takes the request object and returns the response object.

When first created, your function looks like this:

export async function handleRequest(request) {
  /* 
    Implement this function to handle the request. Return the result or throw an error.
    
    > Success case
    return {
      "response": "Your response text",
      "data": {},
      "metadata": {}
    };
    
    > Failure case (throw an error)
    throw new asappUtilities.APIConnectionError({
      customErrorCode: "SOME_ERROR",  // Codes make it easier to specify logic to GenerativeAgent around error handling
      errorMessage: "Error message", // This is the human readable error message
      error: [Optionally pass along error object]
    });
  */
  throw new asappUtilities.APIConnectionError({
    customErrorCode: "NOT_IMPLEMENTED"
  });
}

Replace the NOT_IMPLEMENTED error with your own implementation for the API connection.

Function Parameters

The request parameter contains the data defined in your request schema.

Return Value

Your function should return an object that matches your response schema.

Error Handling

We expose several error classes in the asappUtilities library. Always use one of these error classes for error handling to ensure proper error propagation to ASAPP:

try {
  // Your API logic here
  return {
    response: "Success message",
    data: resultData,
    metadata: {}
  };
} catch (error) {
  throw new asappUtilities.APIConnectionError({
    customErrorCode: "API_ERROR",
    errorMessage: "API call failed",
    error: error
  });
}

ASAPP Utilities

ASAPP Utilities is a library designed for integration of Code Driven API Connections. It provides tools for writing secure and efficient code.

Making API Calls

The library provides a secure way to make API calls to allowed domains. This is the only way to make external HTTP API calls from your code.

const response = await asappUtilities.callAPI(`${asappUtilities.getEnvVariable("API_URL")}/data`, {
  method: "GET",
  headers: {
    "Content-Type": "application/json"
  },
  authMethods: {
    prod: "Production API Auth",
    sandbox: "Sandbox API Auth"
  }
});
const data = await response.json();

Environment Variables

Access environment variables configured during setup. Use environment variables for configuration data like API URLs. Do not use environment variables for sensitive credentials.

For API credentials and authentication data, use Authentication Methods.

const apiUrl = asappUtilities.getEnvVariable("API_URL");
console.log(`Using API URL: ${apiUrl}`);

ASAPP Utilities Errors

In order to properly propagate errors to ASAPP, you must throw an asappUtilities error:

Context Data

When an API Connection is executed, there may be context data from the ASAPP system and this is available to you in the context object.

const contextData = asappUtilities.getContextData();
console.log(`Current ASAPP conversation ID: ${contextData.asapp.conversationId}`);

Fetch Authentication Method Data

To use authentication methods for an API call, provide them as part of the request to asappUtilities.callAPI().

There may be times you want to pull out data from the authentication method, such grabbing claims from a JWT token. You can access the authentication methods you’ve configured in your code using the asappUtilities.getAuthMethod() function.

const authMethodResults = asappUtilities.getAuthMethod({
  prod: "prod-auth-method-1",
  sandbox: "sandbox-auth-method-2",
});

// returns array of type {  headers: Record<string, string>; authResultContext?: object; ttlSeconds?:number;}
console.log(`Current authMethodResults: ${authMethodResults}`);

Using Authentication Methods

Best Practice: Always use authentication methods instead of storing API keys in environment variables. Authentication methods provide better security, easier management, and support for complex authentication flows like OAuth, JWT, and custom token management.

If your API connection requires authentication, first you must add the authentcation method in Settings > Authentication Methods. Then, when making API calls with asappUtilities.callAPI(), specify which authentication method to use by name for each environment:

const response = await asappUtilities.callAPI(
  "https://api.example.com/data",
  {
    method: "GET",
    headers: {
      "Content-Type": "application/json"
    },
    authMethods: {
      prod: "Your Production Auth Method Name",
      sandbox: "Your Sandbox Auth Method Name",
    },
  }
);

The authMethods object maps environment names to the exact names of your configured authentication methods. ASAPP automatically applies the appropriate authentication (e.g. tokens, api keys, etc.) to the headers based on the method you’ve configured for each environment.

Important: The authentication method names in your code must exactly match the names you gave them when creating the authentication methods in the SettingsAuthentication Methods section.

Running your code

While developing your code-based API connection, you can test it by running it in the Run panel. This will allow you to test your code without having to publish it. Any console.log statements will be displayed in the Run panel.

To specify the data that will be passed to your function when running, in the right hand run panel, you need to specify:

  • The Request object that will be passed to your function. This must match the request.json schema.
  • The Context object that will be passed to your function.
  • The Auth object that will be passed to your function. This is only used if the authentication method you are using uses client authentication data.

Additionally, you can specify the Environment to run your code in in the Environment dropdown.

Examples

Simple API Call

export async function handleRequest(request) {
  const { query, context } = request;
  
  try {
    const response = await asappUtilities.callAPI(
      `https://api.example.com/search?q=${encodeURIComponent(query)}`,
      {
        method: "GET",
        headers: {
          "Content-Type": "application/json"
        },
        authMethods: {
          prod: "Production API Auth",
          sandbox: "Sandbox API Auth"
        }
      }
    );
    
    if (!response.ok) {
      throw new Error("API call failed");
    }
    
    const data = await response.json();
    
    return {
      response: `Found ${data.results.length} results for "${query}"`,
      data: data.results,
      metadata: {
        totalResults: data.total,
        query: query
      }
    };
  } catch (error) {
    throw new asappUtilities.APIConnectionError({
      customErrorCode: "API_ERROR",
      error: error
    });
  }
}

Using JWT Claims from Authentication

export async function handleRequest(request) {
  const { query, context } = request;
  
  try {
    // Get authentication method results to access JWT token
    const authMethodResults = asappUtilities.getAuthMethod({
      prod: "Production JWT Auth",
      sandbox: "Sandbox JWT Auth"
    });
    
    // Extract user ID from JWT token (assuming it's in the Authorization header)
    let userId = null;
    if (authMethodResults && authMethodResults.length > 0) {
      const authHeaders = authMethodResults[0].headers;
      const authHeader = authHeaders['Authorization'] || authHeaders['authorization'];
      
      if (authHeader && authHeader.startsWith('Bearer ')) {
        const token = authHeader.substring(7);
        // Decode JWT payload (this is a simplified example - in practice, you'd want proper JWT validation)
        try {
          const payload = JSON.parse(atob(token.split('.')[1]));
          userId = payload.user_id || payload.sub;
        } catch (e) {
          console.log('Could not decode JWT token');
        }
      }
    }
    
    // Build API URL with user ID if available
    let apiUrl = `https://api.example.com/data?customer=${context.externalCustomerId}`;
    if (userId) {
      apiUrl += `&user=${encodeURIComponent(userId)}`;
    }
    
    const response = await asappUtilities.callAPI(
      apiUrl,
      {
        method: "GET",
        headers: {
          "Content-Type": "application/json"
        },
        authMethods: {
          prod: "Production JWT Auth",
          sandbox: "Sandbox JWT Auth"
        }
      }
    );
    
    const data = await response.json();
    
    return {
      response: `Customer data retrieved successfully${userId ? ` for user ${userId}` : ''}`,
      data: data,
      metadata: {
        customerId: context.externalCustomerId,
        userId: userId
      }
    };
  } catch (error) {
    throw new asappUtilities.APIConnectionError({
      customErrorCode: "AUTH_ERROR",
      error: error
    });
  }
}

Additional Libraries

Currently, code-based API connections support the core ASAPP Utilities library and does not allow use of third-party libraries e.g. using require() or import statements.

If you require additional third-party libraries or tools for your integration, reach out to your ASAPP account team to discuss your specific needs.