BOSS.Tech BOSSi Hackathon Guide · v1.0
Menu
Walkthrough: QuickBooks
01 Open the Builder 02 Navigate to BOSSi 03 Select Integration 04 User Permissions 05 OAuth: Basic Config 06 OAuth: Tokens & Webhook 07 OAuth: Trigger Steps 08 Schema: Basic Config 09 Schema: Actions List 10 Action: sync 11 Action: invoices 12 File Tree 13 Action Folders 14 Deploy & Merge
Vibe Code
15 Open Vibe Coding 16 AI Response 17 Debug: Test Action 18 Reading Logs 19 Log Types
Quick Links
Code Reference Config Reference FAQ
Documentation
BOSS.Tech Overview BOSSi Docs BOSSi APIs MiniApps Flows Insights
BOSSi Hackathon Guide

Build Your First Integration

Step-by-step walkthrough using the Builder and Vibe Code at dev.boss.tech. Real example with QuickBooks.

Getting Started

Before You Build

Complete these quick setup steps before using the Developer Portal.

1. Create your BOSS.Tech account

Create your account on app.boss.tech or download BOSS.Tech.

2. Create a company

Set up a company workspace inside BOSS.Tech. This is where everything you build will live.

3. Add a web path to create your web.boss.tech page

Only company owners (the person who created the company) can create the webpage.

Option 1 — In the mobile app

Select your company, open the hamburger menu in the top right, choose Edit Company, then select Create a Website. We auto-fill your company name, but you can customize it. Hit Next and you're ready to build.

Option 2 — In the Developer Portal

Sign in to dev.boss.tech, choose the correct company workspace in the top right, then go to Dashboard > Web Platform. Save or customize your web path, or connect a custom domain.

4. Log in to dev.boss.tech

Use the same account to access dev.boss.tech and start building.

You’re in. Let’s build.
Part 1 — Hands-On

Walkthrough: Building a QuickBooks Integration

This section walks you through a real integration setup step by step. Each screenshot shows exactly what you see in the Builder and what to do next.

Step 1 — Open the Builder

Go to dev.boss.tech and sign in. You land on the Builder home screen.

Builder home screen
Fig 1 — Builder home screen at dev.boss.tech
Element What it does
Top nav bar Main menu. Builder is highlighted in cyan — you are in the right place.
"Select a integration" dropdown Teal-bordered dropdown at top left. Click to pick an existing integration or use the sidebar to create one.
Center — "No files found" File editor is empty until you select or create an integration.
Debug Opens the debug panel for testing action executions.
Vibe Coding AI-assisted code editor for writing Python action scripts.
New Integration Creates a new integration from scratch. Start here.
Resources (right) Quick links to docs: "What is an Integration?" and "What is a MiniApp?"

Step 2 — Navigate to BOSSi Integrations

Click Builder in the top nav. A dropdown shows all available modules.

Builder dropdown menu
Fig 2 — Builder dropdown showing available modules
Module Purpose
MiniApp Build interactive UIs using BOSS Blocks and JSON schemas.
MiniApp Services Backend services that normalize data across integrations.
BOSSi Integrations Build third-party connectors. Select this one.
Flows Automated workflows chaining actions across integrations.
Insights Analytics and reporting.
Vibe AI-assisted development environment.
Select "BOSSi Integrations" to enter the integration builder where all configuration and code editing happens.

Step 3 — Select an Integration

Click the "Select a integration" dropdown in the top left of the sidebar. A list of your existing integrations appears. Select quickbooks.

Once selected, the main panel loads the General Settings page for that integration.

General Settings — QuickBooks selected
Fig 3 — Selecting QuickBooks from the integration dropdown. The General Settings panel loads on the right.
Element What it does
Integration dropdown Lists all integrations in your account. Here you can see newbt, operators, quickbooks, stripe, stripebt. Select the one you want to edit.
Friendly Name The display name users see when connecting. Set to QuickBooks.
Integration Domain The unique reverse-domain identifier: tech.boss.quickbooks. This cannot be changed after creation.
Icon URL The integration's icon. Points to an uploaded asset: tech.boss.quickbooks.png. The QuickBooks logo appears as a preview.
Public toggle When enabled (cyan), the integration is visible and available for companies to connect.
Left sidebar Navigation for this integration: General, User Types, OAuth, Schema, and the file tree for action code.

Step 4 — Configure User Permissions

Click User Types in the left sidebar. This screen defines which user roles can trigger actions in this integration.

User Permissions Setup
Fig 4 — User Permissions Setup showing all configured roles for QuickBooks.
Element What it does
Role list Each row is a user role. Click the expand icon on the right to see and edit which actions that role can trigger.
owner Company owner. Bypasses restrictions — always has full access regardless of what you configure here.
employee, standardNoAccess, etc. QuickBooks-specific roles mapped from the service. Each one can be assigned a different set of allowed actions.
companyAdmin Administrative role with broader access.
bt_customer-user-type-dropdown Internal BOSS.Tech role for platform-level operations.
+ Add new Add a custom role if the defaults don't cover your needs.
This step is optional. Most integrations don't need custom role restrictions. If you skip this, all roles can trigger all actions.

Step 5 — OAuth Setup: Basic Configuration

Click OAuth in the left sidebar. This is where you configure how companies authenticate with the external service. The Basic Configuration tab is selected by default.

OAuth Setup — Basic Configuration
Fig 5 — OAuth Setup showing the Basic Configuration tab for QuickBooks.
Field What it does
Integration Name Display name: quickbooks. Shown to users during the connection flow.
Base URL The OAuth authorization URL: https://appcenter.intuit.com/connect/oauth2. This is where users get redirected to grant access.
Client Id / Client Secret Your OAuth credentials from the QuickBooks developer portal. These authenticate your app with Intuit's OAuth server.
OAuth Required toggle When off, shows "Not an OAuth" — meaning API key auth. For QuickBooks this stays off in the UI but the OAuth flow is configured via the fields above. The toggle controls the isNonOauth flag.
Parameters (JSON editor) OAuth query parameters sent during the authorization redirect. Contains client_id, response_type: "code", scope: "com.intuit.quickbooks.accounting", redirect_uri: "{{generic_callback}}", and state: "{{company_id}}".

Step 6 — OAuth Setup: Tokens and Webhook

Scroll down on the same OAuth page to see the token configuration and webhook settings.

OAuth Setup — Tokens section
Fig 6 — OAuth configuration continued: Required Query Parameters, Token URL, Tokens JSON, and Generate Webhook toggle.
Field What it does
Required Query Parameters Fields the user must provide when connecting. Empty for QuickBooks since OAuth handles everything automatically.
Token URL https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer — the endpoint BOSS.Tech calls to exchange the authorization code for access and refresh tokens.
Tokens (JSON editor) Declares which fields to store from the OAuth response. QuickBooks stores: realmId (the company identifier in QuickBooks), x_refresh_token_expires_in, access_token, and refresh_token. Each uses {{variable}} syntax to capture the value from the OAuth response.
Generate Webhook (toggle) Enabled (cyan). Creates a unique inbound webhook URL for this integration instance. Shows "Webhook configuration generated" when active.
The Tokens block is critical. The realmId field is QuickBooks-specific — it identifies which QuickBooks company the user connected. Your action code accesses it as integration_variables["realmId"]. The Generate Webhook toggle at the bottom creates a unique inbound URL for this integration instance.

Step 7 — OAuth Setup: Trigger Steps

Click the Trigger Steps tab at the top of the OAuth Setup page. This defines what happens immediately after a user successfully connects.

OAuth Trigger Steps
Fig 8 — Trigger Steps tab showing the post-connection action.
Element What it does
Action dropdown Set to quickbooks-sync-v1. This is the action that runs automatically right after the user completes the OAuth flow.
Payload (JSON editor) Contains {} — an empty object. The sync action doesn't need any input data for the initial run; it pulls everything.
+ Add Step You can chain multiple actions to run after connection. For example, sync contacts first, then sync invoices.
Delete icon (trash) Remove this trigger step.
This is the typical pattern: after OAuth completes, trigger an initial sync action that pulls the user's existing data from the service into BOSS.Tech. For QuickBooks, quickbooks-sync-v1 fetches all accounting data on first connect.

Step 8 — Schema: Basic Configuration

Click Schema in the left sidebar. The Basic Configuration tab shows the integration's identity, variables, and security settings.

Schema Basic Configuration
Fig 9 — Schema Setup: Basic Configuration showing name, version, variables, and security block.
Field What it does
Integration Name / Version Quickbooks Generic Integration version 1.0.0. Metadata for identification.
Description Human-readable description of the integration.
Variables (JSON editor) Static values available in all actions via variables["url"]. Here it defines the QuickBooks API base URL: https://quickbooks.api.intuit.com/v3/company/{{realmId}}/reports. Notice {{realmId}} — this gets replaced at runtime with the value stored during OAuth.
Security (JSON editor) Configures how BOSS.Tech authenticates API requests. Shows type: "bearer", token: "{{access_token}}", accessTokenVariable: "access_token", expiryInVariable: "expires_in". BOSS.Tech uses this to automatically attach auth headers and refresh tokens when they expire.
The Variables block is powerful. Any {{variable}} placeholder in the URL gets resolved at runtime from stored tokens. Here, {{realmId}} is the QuickBooks company ID that was captured during OAuth and stored in the tokens block.

Step 9 — Schema: Actions List

Click the Actions tab. This shows every operation the integration supports.

Schema Actions list
Fig 10 — Actions tab listing all available actions for QuickBooks. Each row is an action you can expand and configure.
Element What it does
Search bar Filter actions by name. Useful when you have many actions.
Action list Each row is a declared action: fetch-books-profit-and-loss-v1, fetch-books-cashflow-v1, fetch-books-balance-sheet-v1, etc. Click the expand icon to configure.
+ Add Action Create a new action. You name it, set its flags, and point it to a code step.

Step 10 — Action Configuration: quickbooks-sync-v1

Expand an action to see its configuration. Here is quickbooks-sync-v1 — the sync action that runs after OAuth and orchestrates the initial data pull.

quickbooks-sync-v1 action configuration
Fig 11 — Configuration for quickbooks-sync-v1: the orchestrator action.
Field What it does
Action Name quickbooks-sync-v1 — the action triggered by the OAuth trigger step.
Send data to backend (skipParser: true) Enabled. This action does NOT send its own output through field mapping. Instead, it calls other actions internally using send_to_trigger() — those child actions handle their own data mapping.
Sends data to app (returnsData: false) Disabled. This action doesn't return data to a synchronous caller. It's a background orchestrator.
Code Reference quickbooks-sync-v1 — points to the Python file at action/quickbooks-sync-v1/code/quickbooks-sync-v1.py.
Continue on Failure Disabled. If this step fails, execution stops.
Rate Limit Period: 0, Limit: 0 — no rate limiting on this action.
Why skipParser is true here: This sync action is an orchestrator. It doesn't send data to BOSS.Tech directly — it calls child actions like invoices-post-v1 using send_to_trigger(), and those child actions handle their own field mapping and data storage.

Here is how the sync action calls child actions internally:

Code showing send_to_trigger for invoices
Fig 12 — Code from quickbooks-sync-v1 showing how it iterates over invoices and triggers invoices-post-v1 for each one.
Line What it does
for invoice in invoices: Loops through each invoice fetched from QuickBooks.
logging.info(f"[QB] Sending Invoice → {invoice.get('Id')}") Logs each invoice being processed. Visible in the Builder debug panel.
send_to_trigger("invoices-post-v1", {"body": {"Invoice": invoice}}) Queues invoices-post-v1 as a child action for each invoice. Each runs independently after the sync finishes.
except Exception as ex: Error handling — logs failures without stopping the loop.

Step 11 — Action Configuration: invoices-post-v1

Now look at invoices-post-v1 — one of the child actions called by the sync. Notice the different flag configuration.

invoices-post-v1 action configuration
Fig 13 — Configuration for invoices-post-v1: a data action that sends records to BOSS.Tech.
Field What it does
Action Name invoices-post-v1 — receives individual invoice data from the sync orchestrator.
Send data to backend (skipParser: false) Disabled. This action's output DOES go through field mapping. The data gets mapped and stored in BOSS.Tech.
Sends data to app (returnsData: false) Disabled. Data goes to storage, not returned to a caller.
Key difference between the two actions:

quickbooks-sync-v1 — skipParser: true, returnsData: false. It's an orchestrator that calls other actions. It doesn't store data itself.

invoices-post-v1 — skipParser: false, returnsData: false. It receives data and sends it through field mapping into BOSS.Tech's backend storage. This is where the actual data lands in the app.

Step 12 — File Tree: Integration Structure

In the left sidebar, expand the quickbooks folder under the integration name. This shows the complete file structure of the integration.

File tree — integration root
Fig 14 — The file tree showing the QuickBooks integration structure.
Item What it is
action/ folder Contains all action folders. Each action has its own subfolder with code and an optional schema.json for field mapping.
assets/ folder Static assets like the integration icon (e.g., the QuickBooks logo).
oauth.json Connection flow configuration (what we set up in Steps 5-7).
schema.json Root schema with actions, variables, and security (Steps 8-9).
usertypes.json Role-based permissions (Step 4).
webhook.json Inbound event routing configuration.
Three-dot menu (...) Options for the folder: Merge and Delete.
+ button Create a new file or folder inside.
Rocket icon Deploy this file or folder to the current branch.

Step 13 — File Tree: Inside the Action Folder

Expand the action/ folder to see all action subfolders. Each one corresponds to an action declared in schema.json.

Action folders expanded with context menu
Fig 15 — Action folder expanded showing individual action subfolders. The three-dot menu shows Merge and Delete options.

Each action folder (e.g., bills-post-v1, bulk-fetch-files-v1, delete-integrations-v1) contains the code and configuration for that specific action.

Expand an individual action to see its internal structure:

Action internal structure
Fig 16 — Inside bills-post-v1: the code/ folder contains the Python script, and schema.json defines the field mapping.
Item What it is
code/ folder Contains the Python script(s) for this action. The file name matches the codeRef in schema.json.
bills-post-v1.py The Python script with the main(storage) function. This is where the action logic lives.
schema.json The action-level field mapping schema. Defines how output fields map to BOSS.Tech's data model (the equivalentName JSONPath expressions).
+ menu "Add new file inside" or "Add new folder inside" — for adding additional code steps or resources.

Step 14 — Deploying and Merging

Once your code is ready, you have two operations available via the rocket icon and the three-dot menu:

Deploy (Rocket Icon)

Click the rocket icon next to any file or folder to deploy it to the current branch.

Deploy modal
Fig 17 — Deploy modal showing which files will be published to the development branch.
Element What it does
Files list Shows exactly which files will be deployed. Here: bills-post-v1/schema.json and bills-post-v1/code/bills-post-v1.py.
Branch Deploys to the development branch. Changes become visible to everyone using the app on that branch.
Deploy button Confirms and publishes the changes.

Merge (Three-Dot Menu)

Use Merge to promote changes from one branch to another (e.g., development to production).

Merge modal
Fig 18 — Merge modal showing source branch (development) and target branch (production).
Element What it does
Source Branch Where the changes come from: development.
Target Branch Where the changes go: production. The source fully overwrites matching files in the target.
Files list Shows which files will be merged. Here: quickbooks/action/bills-post-v1/code/bills-post-v1.py.
Merge button Confirms and executes the merge.
Merge overwrites. Files in the source branch fully replace matching files in the target branch. If a file doesn't exist in the target, it gets created. There is no diff or conflict resolution — it's a full overwrite.
Part 2 — Vibe Code

Building with Vibe Code

Vibe Code is the AI-assisted development interface inside the Builder. You describe what you want in plain English, and the AI generates production-ready code by reading BOSSi's documentation and the external service's public API docs. No manual research needed.

How Vibe Code Works

Write a prompt

Describe what you need in natural language. The AI reads BOSSi docs + the external API docs automatically.

Review the generated code

The AI returns complete, ready-to-use configuration or Python code with explanations.

Copy to the right file

Paste the generated code into the corresponding file in the Builder file tree (oauth.json, action.py, etc.).

Save and Deploy

Hit Save, then click the rocket icon to deploy. Changes go live immediately.

Debug

Use the Debug panel to test your action, see real-time logs, and fix any issues.

Iterate

If something fails, go back to Vibe Code, describe the fix, paste, deploy, debug again.

Step 15 — Open Vibe Coding

Click Vibe Coding in the left sidebar. The Vibe Code interface opens with a prompt field asking "What are we building?"

Vibe Coding prompt screen
Fig 19 — The Vibe Coding interface. Type your prompt in the text field at the bottom and click the send button.
Element What it does
Prompt field Type what you want to build in plain English. Example: create an OAuth file for Stripe with a hardcoded API key
"Not using your files" toggle When off, the AI generates code from scratch. When on, it reads your existing integration files for context.
Send button (purple arrow) Submits your prompt. The AI processes it and returns generated code.
The AI reads documentation for you. When you ask it to create a Stripe OAuth, it looks up Stripe's API docs and BOSSi's oauth.json reference automatically. You don't need to know the Stripe API structure — the AI figures it out.

Step 16 — Review the AI Response

After submitting your prompt, the AI returns complete code with explanations.

Vibe Coding AI response
Fig 20 — The AI response: a complete oauth.json for Stripe with a hardcoded API key, plus explanations of each field.

In this example, the prompt was: "create an OAuth file for Stripe with a hardcoded API key"

The AI generated:

Generated oauth.json for Stripe
{
  "name": "Stripe",
  "services": ["payments"],
  "isNonOauth": true,
  "tokens": {
    "apiKey": "sk_live_abc123..."
  },
  "genWebhook": true,
  "url": "{{self_url}}",
  "parameters": {
    "self_url": "{{generic_callback}}"
  },
  "requiredQueryParams": [],
  "triggerSteps": [
    {
      "action": "sync-payments-v1",
      "payload": { "full_sync": true }
    }
  ]
}
What the AI decided Why
isNonOauth: true You asked for a hardcoded API key, so it skipped the OAuth flow entirely.
tokens.apiKey hardcoded No {{placeholder}} — the key is baked in. No user input needed at connection time.
requiredQueryParams: [] Empty — since the key is hardcoded, no user prompt is shown.
genWebhook: true Stripe needs a webhook URL to push payment events.
triggerSteps calls sync-payments-v1 After connecting, immediately sync existing payment data.

The AI also provided follow-up suggestions:

Copy this code into the oauth.json file in the Builder file tree, replace the placeholder API key with your real one, save, and deploy.

Step 17 — Debug: Testing Your Integration

Click Debug in the left sidebar. This is where you test actions and see real-time execution logs.

Debug setup screen
Fig 21 — Debug interface showing the integration selection, action dropdown, and payload configuration.
Element What it does
Integration selector Choose which connected integration to test. Here: tech.boss.stripebt[0] — the first Stripe instance.
Action dropdown Select the action to run. Here: stripe-sync-v1.
Connected status Green "Connected" badge confirms the OAuth/API key connection is active.
Company ID / Person ID / Integration UUID Context identifiers for this test run. The copy buttons let you grab these for debugging.
Payload Configuration JSON editor for the action's input data. For a sync action, this is typically empty ({"passthrough": {}, "payload": {}}). For other actions like post-products-v1, you'd include the required fields.
Run button Executes the selected action with the given payload.
Payload depends on the action. A sync action usually needs an empty payload. But an action like post-products-v1 requires specific fields:
Example payload for post-products-v1
{
  "payload": {
    "name": "Hustle Plan",
    "description": "Monthly subscription plan",
    "sku": "HUSTLE-001",
    "type": "subscription",
    "isPublic": true,
    "imageUrl": "https://example.com/hustle.png",
    "productExternalId": "prod_stripe_123"
  }
}

Step 18 — Reading the Debug Logs

After clicking Run, the Log Events panel shows real-time execution results.

After clicking Run, the Log Events panel shows real-time execution results. See Step 19 below for detailed log examples.

Column What it shows
Action The action that was executed. Click the expand arrow to see full details.
Time Stamp When the action ran.
Message Result summary. Success messages or error details.
Code HTTP status code. 200 (green) = success. 500 (red) = server error with details.
Correlation Id Unique ID linking all executions in this request chain. Useful for tracing multi-step actions.

Step 19 — Understanding Debug Logs

After clicking Run, the log events stream in real time. Here is a successful stripe-sync-v1 execution:

Debug log events list
Fig 23 — Real-time log events from a stripe-sync-v1 execution. Each row is a log entry with action, timestamp, message, and status code.
Log entry What it means
DEBUG: Starting execution of action: stripe-sync-v1 The action has been triggered and is starting.
DEBUG: Starting step 1 of 1 Executing the first (and only) code step.
DEBUG: Starting code execution step The Python sandbox is running your code.
DEBUG: Sending to trigger service Your code called send_to_trigger() to queue a child action.
INFO: *---STARTING STRIPE SYNC for CUSTOMERS---* A logging.info() call from your code. Custom messages you write appear here.
INFO: Fetched 100 customers, total so far: 100 Pagination progress — the sync is pulling data in batches.
INFO: Total customers fetched: 386 Final count. The sync completed successfully.

Expanding a Log Entry

Click the arrow on any log row to see the full detail. There are different types of log entries:

INFO Log (logging.info)

Shows your custom log messages with full context about the execution:

INFO log detail expanded
Fig 24 — Expanded INFO log showing the result, action context, integration ID, and log level.
Field What it shows
result.code 200 — the log was recorded successfully.
result.message Your logging.info() message.
data.action Which action produced this log.
data.integration_id The full integration identifier (e.g., tech.boss.stripebt[0]).
data.debug_type "log" — this is a logging statement from your code.
data.level "INFO" — the log level.

Trigger Log (send_to_trigger)

When your code calls send_to_trigger(), the debug panel shows the full payload being sent to the child action:

send_to_trigger log detail
Fig 25 — Expanded trigger log showing the full customer data being sent to people-id-fetch-v4.

This is useful for verifying that the data being passed to child actions is correct. You can see the complete payload including all fields from the external service.

ERROR Log (logging.error)

When an action fails, the log shows a red 500 status code with the error details:

ERROR log detail
Fig 26 — Expanded ERROR log showing a 500 status with the error message and action context.
Field What it shows
result.code: 500 The action failed.
result.message The error message: "[ERROR] Invalid or missing name data".
data.debug_type: "action_error" This is an action-level error, not just a log message.

Using Logging in Your Code

Add logging statements to your action code to track execution and debug issues:

Logging examples in action code
# INFO — track progress and print variables
logging.info(f"*-----------STARTING STRIPE SYNC for {resource.upper()}-----------*")
logging.info(f"Fetched {len(customers)} customers, total so far: {total}")

# ERROR — log failures with response details
logging.error(f"[ERROR] Failed to create product: {response.data.decode('utf-8')}")

# DEBUG — verbose details for troubleshooting
logging.debug(f"Raw API response: {data}")
All logging output appears in the Debug panel. Use logging.info() for general progress tracking, logging.error() for failures, and logging.debug() for detailed data inspection. You can print variables, response bodies, and any data you need to verify.
Part 2 — Vibe Code

Writing Actions with Vibe Code

Actions are the operations your integration performs. Each one is a Python script running in a managed sandbox — no packages to install, no servers to configure. Write, save, done.

How a Code Step Works

Every script defines a main(storage) function. Return a dict to pass data to the next step or back to the caller.

action/sync-contacts-v1/code/action.py
def main(storage):
    api_key = integration_variables["apiKey"]
    base_url = variables["baseUrl"]
    page = payload.get("page", 1)

    response = http.request(
        "GET",
        f"{base_url}/contacts?page={page}",
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        },
    )

    if response.status == 429:
        retry_after = int(response.headers.get("Retry-After", 60))
        schedule_task(retry_after, retries=3)
        return {"error": f"Rate limited. Retry in {retry_after}s"}

    if response.status != 200:
        return {"error": f"API error: {response.status}"}

    data = json.loads(response.data.decode("utf-8"))
    contacts = data.get("contacts", [])

    if contacts:
        send_to_parser("sync-contacts-v1", contacts)

    if data.get("has_more"):
        send_to_trigger("sync-contacts-v1", {"page": page + 1})

    return {"synced": len(contacts)}

Key Functions You Will Use

Function What it does
http.request(method, url, headers, body) Make HTTP calls. Pre-loaded, no import needed.
send_to_parser(action, data) Send records through field mapping into BOSS.Tech.
send_to_trigger(action, payload) Queue the next execution (pagination, fan-out).
schedule_task(seconds, retries) Retry the action after a delay (rate limits).
modify_tokens(tokens) Persist state between runs. Overwrites all tokens — read first, update, write back.
get_token() Get current OAuth access token. Auto-refreshes if expired.
payload Dict with data sent by the trigger.
integration_variables Dict with stored credentials (API keys, tokens).
variables Dict with static values from schema.json.

Create Action (Push to External Service)

action/create-contact-v1/code/action.py
def main(storage):
    api_key = integration_variables["apiKey"]

    contact = {
        "first_name": payload.get("firstName"),
        "last_name": payload.get("lastName"),
        "email": payload.get("email"),
    }

    response = http.request(
        "POST", f"{variables['baseUrl']}/contacts",
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        },
        body=json.dumps(contact).encode("utf-8"),
    )

    if response.status not in (200, 201):
        return {"error": f"Failed: {response.status}"}

    created = json.loads(response.data.decode("utf-8"))
    return {"id": created["id"]}

Available Modules (No Imports Needed)

http

urllib3 PoolManager for HTTP requests

json

JSON encode/decode

logging

Log output visible in Builder debug panel

datetime.datetime

Date and time utilities (accessed as datetime.datetime, not datetime alone)

uuid

UUID generation

urlparse

URL parsing utilities

No imports allowed. The sandbox blocks all import statements. Everything listed above is pre-loaded as globals.
Part 3 — Reference

Configuration Reference

Detailed reference for each configuration file. Expand the sections you need.

oauth.json — Connection Flow

Defines how a company connects to your integration: authentication method, credentials storage, and post-connection actions.

API Key Example

oauth.json
{
  "name": "Northwind CRM",
  "services": ["contacts"],
  "isNonOauth": true,
  "genWebhook": true,
  "tokens": { "apiKey": "{{apiKey}}" },
  "url": "{{self_url}}",
  "parameters": { "self_url": "{{generic_callback}}" },
  "requiredQueryParams": ["apiKey"],
  "triggerSteps": [
    { "action": "sync-contacts-v1", "payload": { "full_sync": true } }
  ]
}

Key Fields

Field Description
isNonOauth Set true for API key auth (no OAuth redirect)
tokens Credentials stored on connect. Available as integration_variables
requiredQueryParams Fields the user must fill in when connecting
genWebhook Generate a unique inbound webhook URL
triggerSteps Actions that run immediately after connecting
services Data routing: contacts, calendar, messages, payments, tickets, ai
schema.json — Actions & Authentication

Declares every operation your integration supports and how it authenticates with the external API.

schema.json
{
  "name": "Northwind CRM",
  "version": "1.0.0",
  "variables": { "baseUrl": "https://api.example.com/v1" },
  "actions": {
    "sync-contacts-v1": {
      "skipParser": false,
      "returnsData": true,
      "steps": [{ "codeRef": "action" }]
    }
  },
  "deleteAction": { "action": "delete-integrations-v1", "payload": {} }
}

Action Fields

Field Description
skipParser false: output goes through field mapping. true: skip mapping.
returnsData true: return value sent to synchronous caller.
rate_limit {"limit": 500, "period": 60} — per integration instance.
deleteAction Runs before integration removal. Clean up webhooks here.
webhook.json — Inbound Events

Routes incoming push notifications from the external service to the right action.

webhook.json
{
  "validations": { "body_required": ["event_type"], "headers_required": [] },
  "triggerSteps": [{
    "condition": "'{{event_type}}' == 'contact.updated'",
    "memory": [{ "key": "cid", "value": "str(body['data']['id'])" }],
    "action": "sync-contacts-v1",
    "payload": { "contact_id": "{{cid}}" }
  }]
}
Field Description
condition Python expression. null = always run.
memory Compute values from body/headers for use in payload.
action Action to trigger.
Field Mapping — Action schema.json

Maps fields from the external service's response to BOSS.Tech's data model. Lives at action/{name}/schema.json.

action/sync-contacts-v1/schema.json
{
  "version": "1.0",
  "data": {
    "integrationPersonId": { "dataType": "string", "required": true, "equivalentName": "$.id" },
    "firstName": { "dataType": "string", "required": false, "equivalentName": "$.first_name" },
    "email": { "dataType": "string", "required": false, "equivalentName": "$.email" }
  }
}

equivalentName is a JSONPath expression. $.id extracts the id field from each record.

usertypes.json — Roles & Permissions

Restricts which actions each role can trigger. Optional — without it, all roles can trigger all actions.

usertypes.json
{
  "owner": ["sync-contacts-v1", "delete-integrations-v1"],
  "admin": ["sync-contacts-v1"],
  "agent": ["sync-contacts-v1"],
  "contributor": []
}

Account owners bypass this file entirely.

Variables — Complete Globals Reference

Data

Global Type Description
payload dict Data sent with the trigger
storage dict Shared across steps in one action run
variables dict Static values from schema.json
integration_variables dict Stored credentials and tokens
all_variables dict All sources merged

Context

Global Description
action Current action name
entity_id / company_id Company's entity ID
person_id User who triggered the action
integration Full identifier, e.g. tech.boss.quickbooks[0]
webhook_url Base inbound webhook URL

Functions

Function Description
get_token() Current access token, auto-refreshes
schedule_task(sec, retries) Reschedule after delay
send_to_trigger(action, payload) Queue new action execution
send_to_parser(action, data) Send data through field mapping
modify_tokens(dict) Replace all stored credentials
create_cron_job(...) Create scheduled cron
dispatch_command(action, payload, passthrough) Call another integration action
b64(string) Base64 encode
Part 4

FAQ

Can I import Python packages?

No. The sandbox blocks all imports. Everything you need is pre-loaded: http, json, datetime.datetime, logging, uuid, urlparse, b64().

How do I persist state between runs?

Use modify_tokens(). Always read integration_variables first, update the copy, then write back. It overwrites everything.

send_to_trigger vs send_to_parser?

send_to_trigger() queues a new action run (pagination, fan-out). send_to_parser() sends data through field mapping into BOSS.Tech storage.

How do I handle rate limits?

Check response.status == 429, then call schedule_task(seconds, retries=3). You can also set rate_limit on the action in schema.json.

How does OAuth token refresh work?

Define refreshTokenUrl in schema.json's security block. In code, just call get_token() — it refreshes automatically.

Where do logs go?

Use logging.info(), logging.error() in your code. Output shows in the Logs panel at dev.boss.tech.

What is deleteAction?

The action BOSS.Tech runs before removing an integration. Without it, webhooks you registered stay active forever after disconnect.