Skip to content

Quick start

This page is for developers who have their VBAPI credentials and want to make a first authenticated call. In about five minutes you log in with POST /user-authentication and list five groups with GET /groups.

Both calls only read data, so the script is safe to run against your production database.

What you need

VBA issues all of these to your organization. If you do not have them yet, see Credentials and environments; the five minutes start once they arrive.

You needUsed for
API keyThe x-api-key header on every call
Client ID and client codeThe vbasoftware-client-id and vbasoftware-client-code headers when you log in
Database nameThe vbasoftware-database header on every call (this page writes it as YOUR_DATABASE)
Username and passwordBasic authentication when you log in

You also need one of: .NET 10 (or .NET 8 with a console project), Python 3.10 or later with requests, or Node 20 or later.

1. Set your environment variables

The samples read your credentials from environment variables, so nothing secret is written into code. Set them in the terminal you run the samples from:

$env:VBAPI_BASE_URL    = "https://vbapi.vbasoftware.com/vbasoftware"
$env:VBAPI_API_KEY     = "your-api-key"
$env:VBAPI_CLIENT_ID   = "your-client-id"
$env:VBAPI_CLIENT_CODE = "your-client-code"
$env:VBAPI_DATABASE    = "YOUR_DATABASE"
$env:VBAPI_USERNAME    = "your-api-username"
$env:VBAPI_PASSWORD    = "your-api-password"
$env:VBAPI_USER_AGENT  = "YourCompany-Integration/1.0"

In production code, load these from your secrets manager instead.

Each script reads these values in its first block. The scripts send a fixed User-Agent of YourCompany-Integration/1.0; change it to name your integration.

var baseUrl = Environment.GetEnvironmentVariable("VBAPI_BASE_URL") ?? "https://vbapi.vbasoftware.com/vbasoftware";
var apiKey = Env("VBAPI_API_KEY");
var database = Env("VBAPI_DATABASE");
const string userAgent = "YourCompany-Integration/1.0";

using var http = new HttpClient();
http.DefaultRequestHeaders.Add("x-api-key", apiKey);
http.DefaultRequestHeaders.Add("vbasoftware-database", database);
http.DefaultRequestHeaders.UserAgent.ParseAdd(userAgent);

2. Log in

POST /user-authentication takes your username and password as Basic authentication, plus your API key, client ID, client code, and database. It returns tokens in data.authenticationResult.

var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{Env("VBAPI_USERNAME")}:{Env("VBAPI_PASSWORD")}"));
using var login = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/user-authentication");
login.Headers.Authorization = new AuthenticationHeaderValue("Basic", credentials);
login.Headers.Add("vbasoftware-client-id", Env("VBAPI_CLIENT_ID"));
login.Headers.Add("vbasoftware-client-code", Env("VBAPI_CLIENT_CODE"));

using var loginResponse = await http.SendAsync(login);
loginResponse.EnsureSuccessStatusCode();
using var loginJson = JsonDocument.Parse(await loginResponse.Content.ReadAsStringAsync());
var loginData = loginJson.RootElement.GetProperty("data");
if (loginData.TryGetProperty("challengeName", out var challenge) && challenge.ValueKind == JsonValueKind.String)
{
    // For example NEW_PASSWORD_REQUIRED: call POST /change-temporary-password with the session value.
    throw new InvalidOperationException($"Login needs an extra step: {challenge.GetString()}");
}
var idToken = loginData.GetProperty("authenticationResult").GetProperty("idToken").GetString();

The idToken is what you send as Authorization: Bearer <idToken> on every other call. expiresIn is its lifetime in seconds; when it runs out, log in again. Authentication and tokens covers token handling in long-running integrations.

If login asks for an extra step

Instead of tokens, the response can contain a challengeName and a session, with authenticationResult empty. The usual challenge is NEW_PASSWORD_REQUIRED, which means the user still has a temporary password. The scripts stop and print the challenge name. Set a permanent password with POST /change-temporary-password, sending userID, newPassword, and the session value, then run the script again.

3. Make your first call

The first call lists groups: GET /groups?page=1&pageSize=5 returns the first page of five. The script prints the paging header and each group's ID and name.

using var request = new HttpRequestMessage(HttpMethod.Get, $"{baseUrl}/groups?page=1&pageSize=5");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", idToken);

using var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
if (response.Headers.TryGetValues("X-Pagination", out var paging))
{
    Console.WriteLine($"Paging: {string.Join("", paging)}");
}
using var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
foreach (var group in body.RootElement.GetProperty("data").EnumerateArray())
{
    Console.WriteLine($"{group.GetProperty("group_ID").GetString()} - {group.GetProperty("name").GetString()}");
}

4. Run the whole script

Copy the complete file for your language and run it in the terminal where you set the environment variables.

Run with dotnet run Quickstart.cs on .NET 10. On .NET 8, create a console app (dotnet new console), paste the file into Program.cs, and run dotnet run.

// VBAPI quick start: authenticate, then list the first five groups.
// .NET 10: dotnet run Quickstart.cs   (.NET 8: paste into a console app's Program.cs)
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

// Step 1: configuration
var baseUrl = Environment.GetEnvironmentVariable("VBAPI_BASE_URL") ?? "https://vbapi.vbasoftware.com/vbasoftware";
var apiKey = Env("VBAPI_API_KEY");
var database = Env("VBAPI_DATABASE");
const string userAgent = "YourCompany-Integration/1.0";

using var http = new HttpClient();
http.DefaultRequestHeaders.Add("x-api-key", apiKey);
http.DefaultRequestHeaders.Add("vbasoftware-database", database);
http.DefaultRequestHeaders.UserAgent.ParseAdd(userAgent);
// End of step 1

// Step 2: log in and get an ID token
var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{Env("VBAPI_USERNAME")}:{Env("VBAPI_PASSWORD")}"));
using var login = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/user-authentication");
login.Headers.Authorization = new AuthenticationHeaderValue("Basic", credentials);
login.Headers.Add("vbasoftware-client-id", Env("VBAPI_CLIENT_ID"));
login.Headers.Add("vbasoftware-client-code", Env("VBAPI_CLIENT_CODE"));

using var loginResponse = await http.SendAsync(login);
loginResponse.EnsureSuccessStatusCode();
using var loginJson = JsonDocument.Parse(await loginResponse.Content.ReadAsStringAsync());
var loginData = loginJson.RootElement.GetProperty("data");
if (loginData.TryGetProperty("challengeName", out var challenge) && challenge.ValueKind == JsonValueKind.String)
{
    // For example NEW_PASSWORD_REQUIRED: call POST /change-temporary-password with the session value.
    throw new InvalidOperationException($"Login needs an extra step: {challenge.GetString()}");
}
var idToken = loginData.GetProperty("authenticationResult").GetProperty("idToken").GetString();
// End of step 2

// Step 3: first call, list five groups
using var request = new HttpRequestMessage(HttpMethod.Get, $"{baseUrl}/groups?page=1&pageSize=5");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", idToken);

using var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
if (response.Headers.TryGetValues("X-Pagination", out var paging))
{
    Console.WriteLine($"Paging: {string.Join("", paging)}");
}
using var body = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
foreach (var group in body.RootElement.GetProperty("data").EnumerateArray())
{
    Console.WriteLine($"{group.GetProperty("group_ID").GetString()} - {group.GetProperty("name").GetString()}");
}
// End of step 3

static string Env(string name) =>
    Environment.GetEnvironmentVariable(name) ?? throw new InvalidOperationException($"Set {name}");

The output looks like this (illustrative; field values are fake):

Paging: {"TotalCount":1,"TotalPages":1,"CurrentPage":1,"PageSize":5}
DOCS-GRP1 - Sample Group One

What you just saw

Example response (illustrative; field values are fake). Real responses include every Groups field; most are left out here.

{
  "data": [
    {
      "group_ID": "DOCS-GRP1",
      "name": "Sample Group One",
      "admin_COBRA": false,
      "advice_To_Pay": false,
      "apply_Ex_Code": false,
      "auth_Not_Reqd_Override": false,
      "coPay_Max_Per_Date_Provider": false,
      "default_Process_by_Disability_Date": false,
      "default_Roll_Up_Waiting_Period": false,
      "disable_COBRA_Auto_Select": false,
      "duplicate_Claim_Override": false,
      "individual_Billing": false,
      "individual_Cycle": false,
      "individual_Dates": false,
      "open_Closed_Enrollment": false,
      "paperless": false,
      "prepaid_Group": false,
      "prorate_Deduction": false,
      "self_Admin": false,
      "set_Billing_Due_Date": false,
      "entry_Date": "2030-01-01T00:00:00",
      "entry_User": "DOCS-USER",
      "update_Date": "2030-01-01T00:00:00",
      "update_User": "DOCS-USER"
    }
  ],
  "error": null,
  "debug": {
    "activityID": "00000000-0000-0000-0000-000000000000"
  }
}

JSON responses are wrapped in the same envelope:

{
  "data": { },
  "error": null,
  "debug": { "activityID": "00000000-0000-0000-0000-000000000000" }
}
  • data holds the result: one object for a get, create, or update, and an array for a list.
  • error is null on success. On failure it is a problem-details object with type, title, status, detail, and instance.
  • debug.activityID identifies the request in VBA's logs. Successful responses spell it activityID; error responses spell it ActivityID, so read the property without regard to case (the sample clients do). Log it with every failure and include it when you contact support.

Not every response has an envelope. 204 No Content (deletes) has no body, some 202 Accepted responses for queued work return only a short confirmation message, and the API gateway answers 401 with {"message":"Unauthorized"} and 403 with {"message":"Forbidden"}.

List calls also return an X-Pagination response header with TotalCount, TotalPages, CurrentPage, and PageSize. Pages start at 1, and pageSize defaults to 100. See Pagination and sorting.

The scripts set these headers for you:

Every API call sends these headers:

HeaderValue
x-api-keyYour API key.
AuthorizationBearer <idToken>, using the ID token from POST /user-authentication.
vbasoftware-databaseThe database to work in, for example YOUR_DATABASE.
User-AgentRecommended. A name for your integration, for example YourCompany-Integration/1.0. VBAPI accepts requests without one today, but a descriptive value lets VBA support find your calls in the logs.
Content-Typeapplication/json when the request has a body.

Logging in (POST /user-authentication) sends a different set:

HeaderValue
x-api-keyYour API key.
AuthorizationBasic <base64 of username:password>.
vbasoftware-client-idYour client ID.
vbasoftware-client-codeYour client code.
vbasoftware-databaseThe database to log in to.
vbasoftware-machine-idOptional. An identifier for the calling machine.
User-AgentRecommended: your integration name.

Check your credentials with curl (optional)

To check your credentials without installing anything, run this one line in bash (Git Bash or WSL on Windows) after setting the environment variables. It logs in once and prints the HTTP status and whether tokens ("authenticationResult":{) or a challenge ("challengeName":"NEW_PASSWORD_REQUIRED") came back. It never prints the tokens, so the output is safe to paste into a support request.

curl -s -X POST "${VBAPI_BASE_URL:-https://vbapi.vbasoftware.com/vbasoftware}/user-authentication" -u "$VBAPI_USERNAME:$VBAPI_PASSWORD" -H "x-api-key: $VBAPI_API_KEY" -H "vbasoftware-client-id: $VBAPI_CLIENT_ID" -H "vbasoftware-client-code: $VBAPI_CLIENT_CODE" -H "vbasoftware-database: $VBAPI_DATABASE" -A "${VBAPI_USER_AGENT:-YourCompany-Integration/1.0}" -w '\nHTTP %{http_code}\n' | grep -oE '"(authenticationResult|challengeName)": *("[A-Za-z_]+"|null|[{])|^HTTP [0-9]+$'

Something went wrong?

What you seeLikely causeFix
401 from POST /user-authenticationWrong username or password, or the wrong client ID, client code, or database for that user.Check each value with whoever issued your credentials.
challengeName: NEW_PASSWORD_REQUIRED instead of tokensThe user still has a temporary password.Set a permanent password with POST /change-temporary-password, then log in again.
401 on a data callThe ID token is missing, expired, or not sent as Bearer.Log in again, and send Authorization: Bearer <idToken>.
403 with {"message":"Forbidden"} on every callThe x-api-key header is missing or wrong.Send your API key in x-api-key on every request, login included.
429More than 25 requests per second (burst 50) on your API key.Slow down and retry with backoff. The samples do this for you.
Login or data calls fail for a database you expect to useThe vbasoftware-database value does not match a database your user can reach.Use the database name you were given, exactly as issued.
Contacting support

Send VBA support:

  • the debug.activityID from the response
  • the date and time of the call, with time zone
  • the method and path, for example POST /claim-batches
  • the full error object from the response
  • the request body, with passwords, tokens, and personal data removed

Never send your password, API key, or tokens. See Support and escalation.

Next steps