This playbook is for integrators who receive authorization (prior approval) requests in another system, such as a utilization management or intake tool, and need to record them in VBA. You end up with an authorization that has its procedure and diagnosis codes attached and a status, ready for claims to be matched against it.
VBAPI clients connect to production, so the write calls on this page create or change real records in the database you name in vbasoftware-database. Try the steps in a non-production database first if VBA has given you one, use clearly fake values while you test, and keep the "Check it worked" calls, which only read data. "Try it" in the API reference also calls production.
| Who this is for | Utilization management and care management integrators |
| Time to run | About 10 minutes |
| Writes data? | Yes. Every step except the read-back creates or changes a record. Try it in a non-production database first if you have one; "Try it" in the API reference also calls production. |
| Endpoints used | 8 |
- You have completed the quick start and can get an ID token.
- The member already exists. You need their
subscriber_IDandmember_Seq(see IDs, keys and field names). Check it withGET /subscribers/{subscriberID}/members/{memberSeq}first:POST /authsdoes not reject a member that does not exist. - The provider already exists. You need VBA's internal
provider_ID, not the provider's NPI. - The procedure code, diagnosis code, and auth code you plan to use are configured in your database. The lookups in step 1 show you how to check.
- Your API user has rights to the authorization screens. Which security areas that requires needs confirmation with VBA.
The samples read your credentials from 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.
The authorization refers to codes that must already exist in your database. Look them up before you build the request:
- Procedure codes:
GET /procedure-codes. - Diagnosis codes:
GET /diagnostic-codes, and the code types (the ICD version) withGET /diagnostic-code-types. - Authorization types, if you set
auth_Type:GET /auth-types. - The auth code you will apply in step 6:
GET /auth-codes/{authCode}. There is no list operation for auth codes, so get the codes your database uses from your VBA administrator.
The samples keep these values in constants at the top of the file. The codes shown (99213, Z0000, diagnosis code type 0, auth code DOCS-A) are placeholders; replace them with codes from your own database.
Send the six required fields plus the member, provider, and requested dates and units. Five of the required fields are booleans that the database defaults to false; send false unless you know otherwise. tPlan_Printed is a one-character string, not a boolean; the database default is "0". Do not send auth_Number: the API generates it.
var auth = new Dictionary<string, object?>
{
// Required by the Auth schema. The server generates auth_Number, so do not send it.
["closed"] = false,
["new_Or_Extended"] = false,
["previous_Treatment"] = false,
["tPlan_DownLoaded"] = false,
["tPlan_Printed"] = "0", // a one-character string, not a boolean
["urgent"] = false,
// The member and provider the request is for.
["subscriber_ID"] = SubscriberId,
["member_Seq"] = MemberSeq,
["provider_ID"] = ProviderId,
["requested_From"] = "2030-01-15T00:00:00",
["requested_Thru"] = "2030-03-15T00:00:00",
["units_Requested"] = 6,
["notes"] = "DOCS sample authorization",
};
var created = await api.PostAsync("/auths", auth);
var authNumber = created.Data!["auth_Number"]!.GetValue<string>();
Console.WriteLine($"Created auth {authNumber}");
The request body, as JSON:
{
"closed": false,
"new_Or_Extended": false,
"previous_Treatment": false,
"tPlan_DownLoaded": false,
"tPlan_Printed": "0",
"urgent": false,
"subscriber_ID": "DOCS-0001",
"member_Seq": "02",
"provider_ID": "DOCS-PRV1",
"requested_From": "2030-01-15T00:00:00",
"requested_Thru": "2030-03-15T00:00:00",
"units_Requested": 6,
"notes": "DOCS sample authorization"
}The full field list is in the Auth schema on the reference page. Which other fields a simple outpatient request needs (for example auth_Type, service_Type, or treatment_Type) depends on how your authorization screens are configured; check with your VBA administrator.
Save from the response: data.auth_Number (used as authNumber in every later step).
Check it worked: the response is 200 with the new authorization in data. Creates return 200, not 201.
POST /auths/{authNumber}/procedure-codes
Add one procedure line per call. auth_Number in the body must match the authNumber in the path, or the API rejects the request. authProcCodes_Key is required by the schema but is assigned by the database, so send 0.
var procedure = new Dictionary<string, object?>
{
["authProcCodes_Key"] = 0, // identity column; the server assigns the key
["auth_Number"] = authNumber, // must match the path
["exclude_Modifier_Match"] = false,
["procedure_Code"] = ProcedureCode,
["units_Requested"] = 6,
};
var addedProcedure = await api.PostAsync($"/auths/{authNumber}/procedure-codes", procedure);
Console.WriteLine($"Added procedure line {addedProcedure.Data?["authProcCodes_Key"]}");
exclude_Modifier_Match controls claim matching: when true, adjudication does not require the claim's procedure modifiers to match modifier1 to modifier4 on this line.
Save from the response: data.authProcCodes_Key, if you will update or delete the line later.
POST /auths/{authNumber}/diagnostic-codes
Add one diagnosis per call. The required fields are auth_Number (matching the path), diagnostic_Code, diagnostic_Code_Type (the ICD version, from step 1), and disable_Provider_Match.
var diagnosis = new Dictionary<string, object?>
{
["auth_Number"] = authNumber, // must match the path
["diagnostic_Code"] = DiagnosticCode,
["diagnostic_Code_Type"] = DiagnosticCodeType,
["disable_Provider_Match"] = false,
["primary_Diag"] = "Y",
};
await api.PostAsync($"/auths/{authNumber}/diagnostic-codes", diagnosis);
The diagnosis key is the combination of auth_Number, diagnostic_Code, and diagnostic_Code_Type, so adding the same code and type twice to one authorization fails.
GET /auths/{authNumber}, GET /auths/{authNumber}/procedure-codes, and GET /auths/{authNumber}/diagnostic-codes
These calls only read data. Use them to confirm what was saved before you change the status.
var saved = (await api.GetAsync($"/auths/{authNumber}")).Data!;
var procedures = (await api.GetAsync($"/auths/{authNumber}/procedure-codes")).Data?.AsArray().Count ?? 0;
var diagnoses = (await api.GetAsync($"/auths/{authNumber}/diagnostic-codes")).Data?.AsArray().Count ?? 0;
Console.WriteLine($"Auth {saved["auth_Number"]}: status={saved["status"]} procedures={procedures} diagnoses={diagnoses}");
Example response from GET /auths/{authNumber} (illustrative; field values are fake, and most of the 94 fields are omitted):
{
"data": {
"auth_Number": "DOCS-0000001",
"subscriber_ID": "DOCS-0001",
"member_Seq": "02",
"provider_ID": "DOCS-PRV1",
"requested_From": "2030-01-15T00:00:00",
"requested_Thru": "2030-03-15T00:00:00",
"units_Requested": 6,
"auth_Code": null,
"status": null,
"closed": false,
"new_Or_Extended": false,
"previous_Treatment": false,
"tPlan_DownLoaded": false,
"tPlan_Printed": "0",
"urgent": false
},
"error": null,
"debug": { "activityID": "00000000-0000-0000-0000-000000000000" }
}POST /auths/{authNumber}/process-status/{newAuthCode}
This call has no body. The API looks up newAuthCode in your database's auth codes and then updates the authorization:
auth_Codeis set to the code, andstatusto that code'sauth_Action(for example approved, denied, pended, or voided).approved_Byis set to your API user andapproved_Dateto the current time.- If the code's action is denied or voided,
units_Approvedandamount_Approvedare reset to0.
An unknown code fails, so the samples check it with GET /auth-codes/{authCode} first.
System.Text.Json.Nodes.JsonNode code;
try
{
code = (await api.GetAsync($"/auth-codes/{NewAuthCode}")).Data!;
}
catch (VbapiException ex)
{
Console.WriteLine($"Auth code {NewAuthCode} is not configured: {ex.Message}");
return;
}
Console.WriteLine($"Applying {code["auth_Code"]} (action {code["auth_Action"]}: {code["description"]})");
var updated = (await api.PostAsync($"/auths/{authNumber}/process-status/{NewAuthCode}")).Data!;
Console.WriteLine($"Auth {authNumber}: auth_Code={updated["auth_Code"]} status={updated["status"]} approved_By={updated["approved_By"]}");
The status change does not set the approved dates or approved units. If your process records them (approved_From, approved_Thru, units_Approved), set them with PUT /auths/{authNumber}. Whether they are required for your workflow needs confirmation with an SME.
Check it worked: the response data.auth_Code equals the code you sent.
Call GET /auths/{authNumber} again. You should see your auth_Code, the matching status, your API user in approved_By, and today's approved_Date. The procedure and diagnosis lists from step 5 should each contain the lines you added.
VBA scores candidate authorizations for each claim line in the CLAIMBATCHAUTHMATCH view (also available as a SearchConfig). For the claim's member, each authorization gets a point for each of these matches:
- Provider: the authorization's
provider_IDis the claim's provider. - Diagnosis: one of the claim's diagnosis codes, with the same code type, is on the authorization, or the authorization has no diagnoses.
- Procedure: the claim line's procedure code and modifiers match a procedure line, or the authorization has no procedure lines.
- Date: the service date falls between
approved_Fromandapproved_Thru.
Separately, two flags tell adjudication what it may ignore when it assigns an authorization automatically: exclude_Modifier_Match on a procedure line (do not require the modifiers to match) and disable_Provider_Match on a diagnosis line (do not require the diagnosis to match). How adjudication combines these to choose an authorization needs confirmation with an SME.
| Symptom | Likely cause | Fix |
|---|---|---|
400 or validation error naming a required field on POST /auths | One of closed, new_Or_Extended, previous_Treatment, tPlan_DownLoaded, tPlan_Printed, or urgent is missing, or tPlan_Printed was sent as a boolean. | Send all six; send tPlan_Printed as "0". |
Error that auth_Number does not match authNumber | The body's auth_Number differs from the path on a procedure or diagnosis call. | Use the auth_Number from step 2 in both places. |
404 from process-status | The auth code is not configured in your database, or the auth number is wrong. | Check the code with GET /auth-codes/{authCode}. |
| Error adding a diagnosis | The same diagnostic_Code and diagnostic_Code_Type are already on this authorization. | Read the list first with GET /auths/{authNumber}/diagnostic-codes. |
Send VBA support:
- the
debug.activityIDfrom the response - the date and time of the call, with time zone
- the method and path, for example
POST /claim-batches - the full
errorobject 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.
- Many authorizations at once:
PUT /authstakes an array ofAuthobjects. Items withoutauth_Numberare created and items with one are updated. It returns207with one result per item, so check each item's status. For codes, usePOST /auths/{authNumber}/procedure-codes-batchandPUT /auths/{authNumber}/diagnostic-codes-batch. - Adjust an approved authorization:
POST /auths/{authNumber}/adjustcreates a new authorization from the original with new approved dates, can copy its procedure and diagnosis codes, shortens the original's approval if the dates overlap, and links the two with an adjustment reference. It returns the new authorization. - Link related authorizations:
POST /auths/{originalAuthNumber}/references. - Safe to re-run? No. Each run of step 2 creates a new authorization with a new
auth_Number. To avoid duplicates, store theauth_Numberagainst your own request ID, and check for it before creating. To remove a test authorization, useDELETE /auths/{authNumber}.
The full runnable samples are in the portal repository under _samples/playbooks/create-authorization/: CreateAuthorization.cs (run with the shared C# runner as CreateAuthorizationPlaybook), create_authorization.py, and createAuthorization.ts. Each uses the shared client helper for its language.
- Concepts: Data model, IDs, keys and field names, Errors and troubleshooting
- Next playbook: Enter claims and adjudicate them
- Terms: Glossary
- Ask the AI: "Using the Create an authorization playbook, write a Python function that creates an authorization with one procedure code and one diagnosis code."