Back to all integrations
API

The REST API

How to authenticate against the AppBO REST API, request the right scopes, and read and write core resources from your own code.

On this page
What it connectsHow to connect itWhat data movesWorked exampleLimits and gotchas
Need this wired up?

Bring your stack to a call and we will connect it, test every field in both directions, and hand it over documented.

Book a call
01

What it connects

The REST API is the programmatic surface for almost everything an AppBO account holds: contacts and their custom fields, conversations and messages, opportunities and pipelines, calendars and appointments, forms and their submissions, workflows, and the sub-account structure an agency uses to keep clients separate. On one side of the connection sits your own code. A backend service, a nightly job, a mobile app, an internal admin tool, or a data pipeline. On the other side sits a single account, or a set of accounts authorized together under one agency-level app installation.

You reach for the API when the behavior you need cannot be expressed as a workflow step or a native connector. Typical cases: your product of record is an external system and AppBO must mirror it; you need to backfill thousands of historical records; you want conversation history inside your own support console; you need to compute something (a score, an eligibility check, a pricing tier) in your own code and write the result back onto the contact so that BO's automations can branch on it.

The API is also the substrate under every other integration in this library. Automation platforms, outbound webhooks and custom code embeds all end up calling the same endpoints with the same tokens and the same limits, so the authentication and error-handling habits you build here carry over directly. Treat the published API reference as the source of truth for exact paths, field names and scope identifiers, and treat this guide as the shape of the work rather than a substitute for it.

02

How to connect it

  1. Decide the blast radius first. A single sub-account integration is authorized against that one account; an agency-level integration is authorized once and then acts across the accounts it has been granted. These produce different tokens and different scope requirements, and retrofitting one into the other is painful.
  2. Register an application in the platform's developer area. Give it a name your team will recognize in an audit, and record the client identifier and client secret it issues.
  3. Choose the authentication model. Prefer the OAuth app flow, which yields a refreshable access token bound to a named account. Only use a long-lived account-level key where the platform offers one and your use case genuinely cannot complete a browser handshake.
  4. Select scopes. Request the narrowest set that covers your calls: read-only scopes where you only read. Copy the exact scope identifiers out of the API reference rather than guessing them; a mistyped scope fails at authorization time, and an over-broad one is a standing risk.
  5. Store the client secret in a secret manager or server environment variable. Never ship it in browser JavaScript, a mobile binary, or a repository.
  6. Complete the authorization handshake: send the user to the platform's authorize URL with your client id, requested scopes and redirect URI; receive the short-lived code on your redirect endpoint; exchange it server-side for an access token and refresh token.
  7. Record what came back alongside the tokens. The account or location identifier, the granted scopes, and the expiry. Your code should always know which account a token speaks for.
  8. Set the headers every request needs: the bearer Authorization header, a JSON Accept header, and the API version header the reference specifies. Omitting the version header is a common cause of responses that look subtly wrong.
  9. Make one low-risk read call to prove the credential resolves: fetch a single contact, or list the account's calendars. A 401 here means the token or header is wrong; a 403 means the token is valid but the scope is missing.
  10. Confirm the account identifier in the response matches the account you intended to touch. In multi-account setups this check catches the worst class of bug before it writes anything.
  11. Implement refresh ahead of expiry, not after failure. Refresh on a timer with a margin, and take a lock so that concurrent workers do not race and invalidate each other's refresh token.
  12. Add retry with exponential backoff and jitter for 429 and 5xx responses, and log the request identifier the platform returns so support conversations have something to anchor on.
  13. Replace polling with outbound webhooks wherever an event exists for what you are watching. Polling contact lists on a short interval is the fastest way to exhaust your rate budget.
  14. Verify end to end: create a throwaway contact through the API, confirm it appears in the AppBO UI with the fields you set, confirm any workflow you expected to fire did fire, then delete it.
  15. Promote the credential to production behind monitoring. Alert on sustained 401 (token broken), sustained 429 (you are over budget), and on refresh failures: those are silent until something user-visible breaks.
03

What data moves

From your system into the platform

  • Contacts: name, email, phone, address, tags, assigned owner, and custom field values defined on the account.
  • Opportunities: pipeline, stage, monetary value, status, associated contact.
  • Conversation content: outbound messages on channels your scopes cover, plus notes and tasks attached to a contact.
  • Appointments: calendar, assigned user, start and end, attendee contact.
  • Workflow entry: adding a contact to a workflow, or firing an inbound trigger that a workflow listens for.

From the platform into your system

  • Contact records and their custom field values, retrievable individually or as filtered pages.
  • Conversation threads and message bodies, with timestamps and direction.
  • Opportunity and pipeline state, for reporting outside the platform.
  • Form and survey submissions, including the field payload as submitted.
  • Calendar definitions, availability and booked appointments.
  • Users and account metadata, for mapping owners between systems.

What does not move

  • Builder artefacts. Funnel and site page layouts, email template design and form styling are authored in the platform and are not meaningfully editable as API payloads.
  • Plan, subscription and billing state for the account itself.
  • Data in any account your token was not granted. There is no implicit cross-account read.
  • Aggregated analytics and dashboard rollups: you can read the underlying records and compute your own, but the rendered reports are not an API resource.
  • Binary attachments as inline JSON. Files generally move through their own upload or media endpoints; check the reference for the current mechanism before assuming a field accepts base64.
04

Worked example

Scenario: an external onboarding app owns the customer record, and AppBO must mirror each new customer so that the nurture automation can run.

  1. A customer completes onboarding in your own app. Your backend writes the record to your database and enqueues a sync job.
  2. The worker picks up the job and asks your token store for a live access token for the target account. The stored token has eleven minutes left, inside the refresh margin, so the worker refreshes it first and persists the new pair.
  3. The worker searches the platform for an existing contact by email, so that a repeat onboarding does not create a second record.
  4. No match is returned, so the worker issues a create-contact call with name, email, phone, the onboarding source, and two custom fields: the internal account identifier and the plan tier name.
  5. The platform responds 201 with the new contact identifier. The worker writes that identifier back onto its own customer row, which becomes the join key for every later update.
  6. The worker makes a second call to add the contact to the onboarding workflow.
  7. Inside the platform, the workflow fires: it sends the welcome email, waits, then sends an SMS if the email went unopened.
  8. Three days later the customer upgrades. Your app enqueues an update job, which this time has the contact identifier and issues a direct update rather than a search: setting the plan tier custom field to the new value.
  9. A workflow in the platform is listening for a change on that custom field and moves the contact's opportunity to the Upgraded stage.
  10. An outbound webhook on the stage change posts back to your app, which records that the mirror is consistent. The round trip is closed without either side polling.
05

Limits and gotchas

Rate limiting

The API enforces both a burst limit and a longer-window quota, and exceeding either returns 429. Check the current published figures in the API reference rather than hard-coding a number from a blog post, and read the rate-limit headers on live responses. They are the authoritative view of your remaining budget. Design for the budget: batch where an endpoint supports it, cache account metadata that rarely changes, and prefer webhooks over polling.

Retries and idempotency

Assume no automatic request de-duplication unless the reference documents an idempotency mechanism for the endpoint you are calling. A create call that times out may well have succeeded, so a blind retry is how you get duplicate contacts. The safe pattern is search-then-create, or keep your own external identifier on a custom field and look it up before writing. Retry only on 429 and 5xx; a 4xx other than 429 will fail identically forever.

Field mapping traps

Custom fields are referenced by identifier, not by the label you see in the UI, and renaming a field in the UI does not change its identifier. Fetch the field definitions for the account at startup and map by identifier. Beware type coercion: a dropdown field will reject a value outside its option set, dates want the format the reference states, and phone numbers are best sent in E.164 so that de-duplication and messaging both behave.

Duplicates

Accounts differ in how aggressively they merge on email or phone. Do not rely on the platform to de-duplicate for you. Pick one key, enforce it on your side, and store the platform's contact identifier the moment you receive it.

Sandbox versus live

Test against a dedicated test sub-account rather than a production one, and keep separate credentials per environment. The sharp edge is that a test account with live workflows and a connected phone number will send real messages to whatever number you typed into a fixture.

Auth expiry

Access tokens are short-lived and refresh tokens can be revoked when a user disconnects the app, changes their password, or the installation is removed. Treat a refresh failure as a re-authorization event that needs a human, not as a transient error to retry forever, and alert on it, because otherwise the integration simply goes quiet.

What commonly breaks

  • A missing or stale API version header changing response shape after a platform update.
  • A scope that was never requested, surfacing as 403 only on the one endpoint you call rarely.
  • Concurrent workers racing a token refresh and invalidating the pair.
  • Pagination assumed to be stable while records are being written underneath the cursor.
  • Code written against one sub-account quietly assuming every account has the same custom fields.

Connect it with us.

We wire the integration, map the fields, test both directions and hand it over with the SOPs your team needs to run it.

Book a call