<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kate Apideck</title>
    <description>The latest articles on DEV Community by Kate Apideck (@kate_apideck).</description>
    <link>https://dev.to/kate_apideck</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2899028%2F004a9b1a-6552-459f-bc74-604f53f732cb.jpg</url>
      <title>DEV Community: Kate Apideck</title>
      <link>https://dev.to/kate_apideck</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kate_apideck"/>
    <language>en</language>
    <item>
      <title>How to Get Your Plaid API Key</title>
      <dc:creator>Kate Apideck</dc:creator>
      <pubDate>Thu, 13 Aug 2026 07:54:01 +0000</pubDate>
      <link>https://dev.to/apideck/how-to-get-your-plaid-api-key-2mk8</link>
      <guid>https://dev.to/apideck/how-to-get-your-plaid-api-key-2mk8</guid>
      <description>&lt;p&gt;Plaid is the financial data infrastructure company that connects applications to bank accounts. If your product needs to pull transaction data or verify bank account ownership, Plaid is probably on your shortlist. Most fintech apps in the US and Canada rely on Plaid to handle the messy reality of connecting to &lt;a href="https://www.openbankingtracker.com/api-aggregators/plaid" rel="noopener noreferrer"&gt;9,600+ financial institutions&lt;/a&gt; through a single API.&lt;/p&gt;

&lt;p&gt;You're here because you need a Plaid API key to connect your app. Here's how to get your credentials and make your first request without reading their entire documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you start, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An email address for your Plaid account (business email preferred for production approval)&lt;/li&gt;
&lt;li&gt;A clear idea of which Plaid products you need (Transactions, Auth, Identity, Balance, etc.)&lt;/li&gt;
&lt;li&gt;Node.js, Python, Ruby, Go, or Java installed if you want to use one of Plaid's official client libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Create a Plaid account
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://dashboard.plaid.com/signup" rel="noopener noreferrer"&gt;https://dashboard.plaid.com/signup&lt;/a&gt; and create an account. Plaid will ask for basic information about you and your company. Complete the signup process and acknowledge their terms of service. Plaid's own &lt;a href="https://plaid.com/docs/quickstart/" rel="noopener noreferrer"&gt;quickstart guide&lt;/a&gt; walks through the full flow if you want the longer version.&lt;/p&gt;

&lt;p&gt;Once you're in, you'll land on the Plaid Dashboard. This is where you manage API keys and production access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Find your Plaid API keys
&lt;/h2&gt;

&lt;p&gt;From the Dashboard, navigate to &lt;strong&gt;Developers &amp;gt; Keys&lt;/strong&gt; (or go directly to &lt;a href="https://dashboard.plaid.com/developers/keys" rel="noopener noreferrer"&gt;https://dashboard.plaid.com/developers/keys&lt;/a&gt;). This is where Plaid stores your API keys.&lt;/p&gt;

&lt;p&gt;You'll see two credentials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;client_id&lt;/strong&gt;: A unique identifier for your application. This stays the same across all environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;secret&lt;/strong&gt;: An environment-specific key. You'll get a separate secret for each Plaid environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/3yExyqaeuKqKGhPY7gVd9v/dd457e40ca148544750d2ffcf8265a7b/Screenshot_2026-03-28_at_10.52.49.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/3yExyqaeuKqKGhPY7gVd9v/dd457e40ca148544750d2ffcf8265a7b/Screenshot_2026-03-28_at_10.52.49.png" alt="Plaid API keys dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Plaid uses two environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sandbox&lt;/strong&gt; (sandbox.plaid.com): Free test environment with simulated bank data. No real financial institutions, no billing. This is where you build and test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production&lt;/strong&gt; (production.plaid.com): Real bank connections, real data, real billing. Requires approval from Plaid before you can access it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your Sandbox secret is available immediately after signup. Copy it now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Understand how Plaid authentication works
&lt;/h2&gt;

&lt;p&gt;Plaid's server-to-server authentication is straightforward. Every API request requires your &lt;code&gt;client_id&lt;/code&gt; and &lt;code&gt;secret&lt;/code&gt;, passed either as headers (&lt;code&gt;PLAID-CLIENT-ID&lt;/code&gt; and &lt;code&gt;PLAID-SECRET&lt;/code&gt;) or in the JSON request body. The full details are in Plaid's &lt;a href="https://plaid.com/docs/api/" rel="noopener noreferrer"&gt;API overview&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There's no OAuth token exchange for server-side API calls. You send your credentials with each request. The OAuth-style flow in Plaid (called Plaid Link) is the client-side component your end users interact with when connecting their bank accounts. That's a separate concern from getting your API keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Store your credentials securely
&lt;/h2&gt;

&lt;p&gt;Add your &lt;code&gt;client_id&lt;/code&gt; and &lt;code&gt;secret&lt;/code&gt; to environment variables. Never hardcode them in your application code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PLAID_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'your_client_id_here'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PLAID_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'your_sandbox_secret_here'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PLAID_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sandbox'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using a &lt;code&gt;.env&lt;/code&gt; file for local development, add it to your &lt;code&gt;.gitignore&lt;/code&gt; immediately. Plaid secrets pushed to a public repository are a real risk, especially once you have production credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Set up billing (when you're ready for production)
&lt;/h2&gt;

&lt;p&gt;Sandbox usage is always free. When you're ready to go live, you'll need to request Production access through the Dashboard by selecting &lt;strong&gt;Migrate to Production&lt;/strong&gt; and filling out the application.&lt;/p&gt;

&lt;p&gt;Plaid offers three &lt;a href="https://plaid.com/pricing/" rel="noopener noreferrer"&gt;pricing tiers&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pay as you go&lt;/strong&gt;: No minimum spend, standard per-use rates. Good for early-stage projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Growth&lt;/strong&gt;: Annual commitment with lower per-use costs. Includes a personal account manager.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom&lt;/strong&gt;: For high-volume usage above $2,000/month. Contact Plaid's sales team.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your first 200 production API calls are free under Plaid's Limited Production access, so you can test with real bank connections before committing to a plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test your Plaid API key
&lt;/h2&gt;

&lt;p&gt;Create a test Item in the Sandbox using Plaid's sandbox-only endpoint. This bypasses the Link UI flow and lets you verify your credentials directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://sandbox.plaid.com/sandbox/public_token/create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"client_id": "YOUR_CLIENT_ID", "secret": "YOUR_SANDBOX_SECRET", "institution_id": "ins_109508", "initial_products": ["transactions"]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful response returns a &lt;code&gt;public_token&lt;/code&gt;. You can then exchange it for an &lt;code&gt;access_token&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://sandbox.plaid.com/item/public_token/exchange &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"client_id": "YOUR_CLIENT_ID", "secret": "YOUR_SANDBOX_SECRET", "public_token": "PUBLIC_TOKEN_FROM_PREVIOUS_STEP"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you get back an &lt;code&gt;access_token&lt;/code&gt; and &lt;code&gt;item_id&lt;/code&gt;, your credentials are working correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security best practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Store your &lt;code&gt;client_id&lt;/code&gt; and &lt;code&gt;secret&lt;/code&gt; in environment variables or a dedicated secrets manager (AWS Secrets Manager or similar).&lt;/li&gt;
&lt;li&gt;Use separate secrets for Sandbox and Production. Plaid generates different secrets per environment by default, so don't mix them up.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;.env&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt; before your first commit. Not after.&lt;/li&gt;
&lt;li&gt;Never expose your &lt;code&gt;secret&lt;/code&gt; in client-side code. All Plaid API calls (except Link initialization) should happen server-side.&lt;/li&gt;
&lt;li&gt;Monitor your Plaid Dashboard for unexpected activity, especially after granting team members access.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common gotchas
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mixing up environment URLs and secrets.&lt;/strong&gt; Your Sandbox Plaid API key only works against &lt;code&gt;sandbox.plaid.com&lt;/code&gt;. Your Production secret only works against &lt;code&gt;production.plaid.com&lt;/code&gt;. If you're getting &lt;code&gt;INVALID_API_KEYS&lt;/code&gt; errors, check that your environment URL and secret match.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confusing Plaid Link with API authentication.&lt;/strong&gt; Plaid Link is the client-side UI widget your end users interact with to connect their bank accounts. It produces a &lt;code&gt;public_token&lt;/code&gt; that you exchange server-side for a permanent &lt;code&gt;access_token&lt;/code&gt;. The &lt;code&gt;client_id&lt;/code&gt; and &lt;code&gt;secret&lt;/code&gt; are your server-to-server credentials, not something you pass to Link directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting to request Production access.&lt;/strong&gt; Sandbox works immediately, but Production requires a separate application through the Dashboard. The approval process includes reviewing your use case, compliance requirements, and may take a few days. Plan for this lead time before your launch date.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not handling the access_token lifecycle.&lt;/strong&gt; Once you exchange a &lt;code&gt;public_token&lt;/code&gt; for an &lt;code&gt;access_token&lt;/code&gt;, that access token remains valid until you explicitly revoke it via &lt;code&gt;/item/remove&lt;/code&gt;. There's no expiration, but you still need to handle error states (like when a user changes their bank password) through Plaid's webhook system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming all institutions support all products.&lt;/strong&gt; Not every bank supports every Plaid product. If you request Auth for an institution that only supports Transactions, Link will fail. Check institution coverage before building your product selection logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building financial integrations beyond banking data
&lt;/h2&gt;

&lt;p&gt;If you're building a fintech application that needs both bank account data and accounting platform connectivity, the two sides of the integration stack are different. Plaid handles the banking layer: account verification and transaction feeds. (For more on how banks and apps connect, see our &lt;a href="https://www.apideck.com/blog/bank-api-integration" rel="noopener noreferrer"&gt;bank API integration&lt;/a&gt; guide.) The &lt;a href="https://www.apideck.com/blog/accounting-integration" rel="noopener noreferrer"&gt;accounting layer&lt;/a&gt;, connecting to platforms like QuickBooks and Xero for invoices and financial reporting, requires a separate integration approach.&lt;/p&gt;

&lt;p&gt;Check out our other API key guides for platforms in the accounting and finance space, including &lt;a href="https://www.apideck.com/blog/how-to-get-your-quickbooks-api-key" rel="noopener noreferrer"&gt;QuickBooks&lt;/a&gt; and &lt;a href="https://www.apideck.com/blog/how-to-get-your-xero-api-key" rel="noopener noreferrer"&gt;Xero&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>plaidapi</category>
      <category>unifiedapi</category>
    </item>
    <item>
      <title>NetSuite OneWorld vs Standard: What Changes When You Hit the API</title>
      <dc:creator>Kate Apideck</dc:creator>
      <pubDate>Tue, 11 Aug 2026 12:49:51 +0000</pubDate>
      <link>https://dev.to/apideck/netsuite-oneworld-vs-standard-what-changes-when-you-hit-the-api-2460</link>
      <guid>https://dev.to/apideck/netsuite-oneworld-vs-standard-what-changes-when-you-hit-the-api-2460</guid>
      <description>&lt;p&gt;Every NetSuite instance is different. That is the first thing any integration team learns, usually the hard way. But the single biggest source of variation across NetSuite accounts has nothing to do with custom fields or SuiteScript extensions. It comes down to one question: is the account running OneWorld or not?&lt;/p&gt;

&lt;p&gt;The answer changes the database schema your integration queries and the currency logic it has to handle. If you build a &lt;a href="https://www.apideck.com/connectors/netsuite" rel="noopener noreferrer"&gt;NetSuite connector&lt;/a&gt; that only works against Standard accounts, it will break the moment a OneWorld customer connects. If you only test against OneWorld, you will throw fatal errors on Standard instances that lack subsidiary tables entirely.&lt;/p&gt;

&lt;p&gt;This post covers what actually differs between the two editions, where the API diverges, and what it means if you are building or buying a NetSuite integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  How OneWorld and Standard editions differ at the schema level
&lt;/h2&gt;

&lt;p&gt;NetSuite Standard runs a single legal entity. One set of books, one base currency. The subsidiary record does not exist. There is no subsidiary table to query, no subsidiary field on transactions, and no intercompany elimination logic.&lt;/p&gt;

&lt;p&gt;NetSuite OneWorld adds multi-subsidiary support. Each subsidiary is a separate legal entity with its own base currency and tax schedules. The chart of accounts can be shared with the parent or customized per subsidiary. Subsidiaries are arranged in a parent-child hierarchy, and financial data rolls up through that hierarchy into consolidated reports.&lt;/p&gt;

&lt;p&gt;Oracle prices OneWorld as a licensing upgrade on top of Standard, typically adding $10,000 to $30,000 per year depending on subsidiary count. Once enabled, the upgrade is permanent and cannot be reversed. Every edition of NetSuite (Light, Mid-Market, Enterprise) can be upgraded to OneWorld.&lt;/p&gt;

&lt;p&gt;The practical difference for integration teams: OneWorld accounts have tables and fields that Standard accounts do not. And querying those missing tables on a Standard account does not return empty results. It throws a fatal error.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the NetSuite OneWorld subsidiary field affects API queries
&lt;/h2&gt;

&lt;p&gt;On a OneWorld account, nearly every transactional and master data record gains a &lt;code&gt;subsidiary&lt;/code&gt; field. Invoices, bills, journal entries, customers, vendors, items, and GL accounts all carry subsidiary references. Some records, like &lt;code&gt;customerSubsidiaryRelationship&lt;/code&gt; and &lt;code&gt;vendorSubsidiaryRelationship&lt;/code&gt;, only exist on OneWorld accounts. Oracle's own REST API documentation states: "You must either use NetSuite OneWorld or have the Subsidiaries hidden feature enabled before you can use this record through REST web services."&lt;/p&gt;

&lt;p&gt;On a Standard account, none of these fields or records exist. A SuiteQL query that JOINs the subsidiary table will fail with a "Record not found" error. A &lt;a href="https://www.apideck.com/blog/integrating-with-the-netsuite-rest-api" rel="noopener noreferrer"&gt;REST API&lt;/a&gt; call to the &lt;code&gt;subsidiary&lt;/code&gt; endpoint returns nothing useful.&lt;/p&gt;

&lt;p&gt;This creates a branching problem. For every resource you want to read from NetSuite (accounts, contacts, transactions, invoices, journal entries), you need at least two query variants: one that includes subsidiary JOINs and filters, and one that does not. Add multi-currency into the mix (which is a separate feature that may or may not be enabled, even on OneWorld accounts), and you need to JOIN the currency table conditionally too. For an &lt;a href="https://www.apideck.com/blog/accounting-integration" rel="noopener noreferrer"&gt;accounting integration&lt;/a&gt; covering 15 or more resources, the Truto engineering team calculated this produces 60+ query variants that need to be managed at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime feature detection for NetSuite OneWorld vs Standard
&lt;/h2&gt;

&lt;p&gt;You cannot ask a NetSuite customer "are you on OneWorld?" and hard-code the answer. Accounts get upgraded. Modules get enabled. The only reliable approach is to probe the account programmatically at connection time.&lt;/p&gt;

&lt;p&gt;The detection pattern is straightforward in concept: attempt to query the subsidiary table, and if the query fails, the account is not running OneWorld. Then cache that result and use it to select the right query templates for every subsequent API call.&lt;/p&gt;

&lt;p&gt;In practice this means your integration layer needs a query routing engine. Every SuiteQL query template exists in at least two forms (with and without subsidiary JOINs), and the correct form gets selected based on the feature flags detected during the initial connection probe. This is architectural work that has to happen before you write a single line of business logic. For a deeper look at the full scope of &lt;a href="https://www.apideck.com/blog/netsuite-integration-guide" rel="noopener noreferrer"&gt;NetSuite integration&lt;/a&gt; complexity, including governance limits and concurrency constraints, see our separate guide.&lt;/p&gt;

&lt;p&gt;The same pattern applies to multi-currency. If the account uses multiple currencies, the &lt;code&gt;currency&lt;/code&gt; table is available and transactions carry currency fields with exchange rate data. If not, querying that table fails. You need to detect and branch for this independently of the OneWorld check, because Standard accounts can enable multi-currency without upgrading to OneWorld.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-currency handling across NetSuite editions
&lt;/h2&gt;

&lt;p&gt;Standard NetSuite with multi-currency enabled supports transactions in foreign currencies against a single base currency. The system converts and posts to the GL in the base currency using the exchange rate at the transaction date.&lt;/p&gt;

&lt;p&gt;OneWorld takes this further. Each subsidiary has its own base currency. A US subsidiary posts in USD, a UK subsidiary in GBP, a Japanese subsidiary in JPY. Transactions record in their original currency and convert to the subsidiary's base currency for GL posting. Consolidated reporting then translates subsidiary financials into the parent's reporting currency using separate consolidated exchange rate tables.&lt;/p&gt;

&lt;p&gt;For API consumers, this means the same invoice can have three currency amounts that matter: the transaction currency (what the customer was billed in), the subsidiary base currency (what hits the local GL), and the consolidated currency (what appears on the parent's P&amp;amp;L). Your integration needs to know which one it is reading and which one it needs. The &lt;code&gt;transactionLine&lt;/code&gt; records in SuiteQL carry fields like &lt;code&gt;fxamount&lt;/code&gt; (foreign currency amount) and &lt;code&gt;amount&lt;/code&gt; (base currency amount), and which "base" that refers to depends on the subsidiary context of the transaction.&lt;/p&gt;

&lt;p&gt;If you are pulling a trial balance or balance sheet through the API, OneWorld accounts require you to specify a subsidiary context for the report. A consolidated trial balance rolls up child subsidiary data through the hierarchy with currency translation at each level. Getting this wrong does not produce an error. It produces wrong numbers, which is worse.&lt;/p&gt;

&lt;p&gt;NetSuite also handles &lt;a href="https://www.apideck.com/blog/tracking-dimensions-in-accounting-integrations" rel="noopener noreferrer"&gt;tracking dimensions&lt;/a&gt; (departments, locations, classes) differently across editions. On OneWorld accounts, departments and locations are scoped to specific subsidiaries, which means your integration needs to validate that a given dimension ID is valid for the subsidiary context of the transaction you are creating.&lt;/p&gt;

&lt;h2&gt;
  
  
  NetSuite SOAP deprecation and the REST API migration timeline
&lt;/h2&gt;

&lt;p&gt;NetSuite does not have one API. It has at least three: &lt;a href="https://www.apideck.com/blog/guide-to-integrating-with-the-netsuite-soap-api" rel="noopener noreferrer"&gt;SuiteTalk SOAP&lt;/a&gt; (being deprecated), SuiteTalk REST, and SuiteQL (accessed through the REST query endpoint). Each has different strengths and gaps.&lt;/p&gt;

&lt;p&gt;SuiteQL is the most capable for read-heavy workloads. It supports JOINs, aggregations, and server-side filtering through SQL-like syntax. The REST Record API is better for individual CRUD operations. And for some operations (generating transaction PDFs, accessing dynamic form metadata), you still need custom SuiteScript RESTlets deployed into the customer's account.&lt;/p&gt;

&lt;p&gt;Oracle is actively winding down SOAP. The 2025.2 release is the last planned SOAP endpoint. Starting with 2026.1, no new SOAP endpoints ship by default. By 2028.2, SOAP will be fully removed. Any integration still running on SOAP at that point will stop working. If you have not started your migration, our &lt;a href="https://www.apideck.com/blog/how-to-get-your-netsuite-api-key" rel="noopener noreferrer"&gt;NetSuite API key guide&lt;/a&gt; covers the authentication setup for both REST and OAuth 2.0.&lt;/p&gt;

&lt;p&gt;The SOAP deprecation interacts with the OneWorld complexity in a specific way: many older integrations handled subsidiary context through SOAP's search and record operations, where the subsidiary field was just another XML element to include or exclude. Migrating those integrations to REST and SuiteQL means rebuilding the query logic from scratch, and the OneWorld branching has to be redesigned at the same time. You cannot simply swap XML payloads for JSON payloads and call it done.&lt;/p&gt;

&lt;p&gt;REST also has known gaps. The subsidiary record itself is read-only through REST (GET only, no POST/PATCH/DELETE). Legacy tax engine features are not exposed through REST at all, which matters because each OneWorld subsidiary can have different tax configurations. SuiteTax is the path forward, but not every NetSuite customer has migrated to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for unified API platforms and ERP integrations
&lt;/h2&gt;

&lt;p&gt;If you are a SaaS company building a &lt;a href="https://www.apideck.com/integrations/netsuite" rel="noopener noreferrer"&gt;NetSuite integration&lt;/a&gt; in-house, you need to handle all of this yourself. That means runtime feature detection and conditional query routing, plus currency normalization and subsidiary-aware data scoping on top. Add ongoing maintenance as Oracle evolves the API surface, and the effort compounds quickly. This is one of the reasons &lt;a href="https://www.apideck.com/blog/erp-api-integration" rel="noopener noreferrer"&gt;ERP API integration&lt;/a&gt; is consistently cited as the most engineering-intensive category of SaaS connectivity.&lt;/p&gt;

&lt;p&gt;If you are using a unified API platform like Apideck, the platform should be handling these differences for you. The value proposition is that you call a single &lt;code&gt;GET /accounting/invoices&lt;/code&gt; endpoint and get a normalized response regardless of whether the underlying NetSuite account is Standard or OneWorld, single-currency or multi-currency, using SuiteTax or the legacy tax engine.&lt;/p&gt;

&lt;p&gt;The quality of a unified API's NetSuite connector comes down to how well it handles this matrix. A connector that only works against Standard accounts, or only against OneWorld accounts, is not production-ready. The feature detection, query branching, and currency normalization have to work across the full range of NetSuite configurations that exist in the wild.&lt;/p&gt;

&lt;p&gt;For Apideck specifically, our &lt;a href="https://www.apideck.com/integrations/netsuite" rel="noopener noreferrer"&gt;NetSuite Accounting API connector&lt;/a&gt; handles OneWorld and Standard accounts through automated feature detection at connection time. The SuiteQL queries adapt based on the detected configuration, and the response schema stays consistent regardless of the underlying edition. Subsidiary data surfaces as a standard field when present and is omitted cleanly when it is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical checklist for NetSuite API integration teams
&lt;/h2&gt;

&lt;p&gt;If you are building or maintaining a NetSuite integration, here is what the OneWorld vs Standard split requires:&lt;/p&gt;

&lt;p&gt;Detect OneWorld and multi-currency status at connection time by probing for table availability. Cache those flags and use them for query routing throughout the integration lifecycle.&lt;/p&gt;

&lt;p&gt;Maintain parallel SuiteQL query templates for every resource: one with subsidiary JOINs and one without. Do the same for currency JOINs where relevant.&lt;/p&gt;

&lt;p&gt;Scope data reads by subsidiary when working with OneWorld accounts. Pulling all data across all subsidiaries without filtering will return a superset that may not match what the customer expects for a given legal entity.&lt;/p&gt;

&lt;p&gt;Handle currency fields explicitly. Know the difference between transaction currency, subsidiary base currency, and consolidated reporting currency. Map each to the correct field in your normalized schema.&lt;/p&gt;

&lt;p&gt;Test against both editions. If your test environment only has OneWorld enabled, you are missing half the failure modes. Spin up a Standard test account and run your full test suite against it.&lt;/p&gt;

&lt;p&gt;Plan for the SOAP sunset. If any part of your NetSuite integration still uses SOAP, the 2028.2 removal deadline is fixed. Build new integrations on REST and SuiteQL with OAuth 2.0 authentication.&lt;/p&gt;

&lt;p&gt;Track REST API gaps. Some OneWorld-specific features (subsidiary record writes, legacy tax fields) are not available through REST. Know where you need SuiteScript RESTlets to fill the gaps, and plan for Oracle to close those gaps over time as REST reaches parity.&lt;/p&gt;

&lt;p&gt;NetSuite is one of the &lt;a href="https://www.apideck.com/blog/legacy-and-established-erp-platforms" rel="noopener noreferrer"&gt;most widely deployed ERPs&lt;/a&gt; in the world, and the OneWorld vs Standard split is one of its most consequential integration variables. Getting this right is the difference between an integration that works for your first three customers and one that works for your next three hundred.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How do I detect whether a NetSuite account is running OneWorld or Standard via the API?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Probe the subsidiary table at connection time using a SuiteQL query like &lt;code&gt;SELECT id FROM subsidiary LIMIT 1&lt;/code&gt;. If the query returns results, the account is running OneWorld. If it throws a "Record not found" error, the account is Standard. Cache this result and use it to select the correct query templates for all subsequent API calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does the NetSuite REST API behave differently on OneWorld vs Standard accounts?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. On OneWorld accounts, records like invoices, customers, and vendors carry a &lt;code&gt;subsidiary&lt;/code&gt; field, and records like &lt;code&gt;customerSubsidiaryRelationship&lt;/code&gt; become available. On Standard accounts, these fields and records do not exist. The subsidiary REST record itself is read-only (GET only). Your integration needs conditional logic to include or exclude subsidiary-related fields depending on the edition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can a Standard NetSuite account use multi-currency without upgrading to OneWorld?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Multi-currency is a separate feature that can be enabled on Standard accounts. When enabled, transactions can be created in foreign currencies and converted to the single base currency at the exchange rate on the transaction date. OneWorld adds the ability for each subsidiary to have its own base currency, which introduces additional currency translation layers for consolidated reporting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens to my NetSuite SOAP integration when Oracle removes SOAP in 2028?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The 2025.2 SOAP endpoint is the last planned release. Starting with 2026.1, no new SOAP endpoints ship. By 2028.2, all SOAP endpoints will be removed and existing SOAP integrations will stop working. Oracle recommends migrating to REST web services with OAuth 2.0 authentication. For OneWorld accounts, this migration also requires rebuilding subsidiary-aware query logic in SuiteQL.&lt;/p&gt;

</description>
      <category>api</category>
      <category>netsuite</category>
      <category>unifiedapi</category>
      <category>netsuiteapi</category>
    </item>
    <item>
      <title>How to Build ERP Extensions: A Developer Guide to NetSuite, SAP, Business Central, Acumatica, and Sage Intacct</title>
      <dc:creator>Kate Apideck</dc:creator>
      <pubDate>Mon, 10 Aug 2026 10:23:10 +0000</pubDate>
      <link>https://dev.to/apideck/how-to-build-erp-extensions-a-developer-guide-to-netsuite-sap-business-central-acumatica-and-3e14</link>
      <guid>https://dev.to/apideck/how-to-build-erp-extensions-a-developer-guide-to-netsuite-sap-business-central-acumatica-and-3e14</guid>
      <description>&lt;h3&gt;
  
  
  NetSuite Customization and ERP Extensions: SuiteScript, Business Central AL, SAP BTP, Acumatica C#, and Sage Intacct Platform Services.
&lt;/h3&gt;

&lt;p&gt;Every ERP vendor has its own answer to the same question: how should third-party developers add functionality without breaking upgrades? The answers vary significantly in tooling and language, but the underlying tension is consistent. Custom code that runs inside the ERP core couples tightly to the ERP's release cycle. Custom code that runs outside connects through APIs and is more stable over time.&lt;/p&gt;

&lt;p&gt;This post covers the four platforms where this tradeoff plays out most frequently for Banks, Fintechs and vertical SaaS teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  NetSuite
&lt;/h2&gt;

&lt;p&gt;NetSuite's extension framework is SuiteCloud. The primary development language is SuiteScript 2.1, which is JavaScript-based. Extensions run inside NetSuite's infrastructure under strict resource limits: script execution time is capped and memory ceilings affect batch processing. A SuiteApp that passes sandbox testing can fail in production under real transaction volume.&lt;/p&gt;

&lt;p&gt;SuiteScript provides several script types for different contexts. User Event scripts fire on record load, validate, and save. Client Scripts run in the browser and respond to field changes. Suitelets are full server-side pages hosted on NetSuite's domain, accessible via URL and iFrameable into other views. Portlets render as panels on the NetSuite dashboard.&lt;/p&gt;

&lt;p&gt;A concrete example: a revenue operations ISV might build a Suitelet that shows a customer's payment history alongside a real-time credit limit recommendation from an external risk model. The Suitelet fetches transaction data via &lt;code&gt;nlapiSearchRecord&lt;/code&gt;, calls the external risk API, and renders an HTML table inside the NetSuite interface. Sales reps access it via a button injected into the Customer record through a Client Script. The whole thing deploys as a SuiteApp through SuiteCloud Development Framework (SDF) and can be listed on the SuiteApp Marketplace.&lt;/p&gt;

&lt;p&gt;For API access, &lt;a href="https://www.apideck.com/connectors/netsuite" rel="noopener noreferrer"&gt;NetSuite&lt;/a&gt; uses &lt;a href="https://dev.to/blog/integrating-with-the-netsuite-rest-api"&gt;Token-Based Authentication (TBA)&lt;/a&gt; for machine-to-machine calls and OAuth 2.0 for user-facing apps. SuiteCloud 2026.1 (February 2026) brought REST web services to functional parity with SOAP, which had previously been required for certain advanced operations. For SuiteApps that expose NetSuite data to external systems, REST-first is now a complete option without the SOAP dependency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Microsoft Dynamics 365 Business Central
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.apideck.com/connectors/microsoft-dynamics-365-business-central" rel="noopener noreferrer"&gt;Business Central&lt;/a&gt;'s extension model runs on AL (Application Language), a proprietary language Microsoft introduced when it moved the platform to the cloud. AL is the only supported path for customizing BC logic. You write it in Visual Studio Code with the AL Language extension, publish apps to a sandbox or production tenant, and distribute through Microsoft's AppSource marketplace for cloud deployments.&lt;/p&gt;

&lt;p&gt;AL uses an event-based pattern: the framework publishes events at key points in standard business processes, and your extension subscribes to them. Adding fields to a table requires a table extension object; modifying a page requires a page extension. Business logic lives in codeunits. Subscribing to &lt;code&gt;OnAfterPostSalesOrder&lt;/code&gt; to trigger a third-party fulfillment workflow is a common pattern, and it's upgrade-safe because extensions can't modify base objects directly, only layer on top of them.&lt;/p&gt;

&lt;p&gt;For UI extensions that go beyond field injection, BC offers control add-ins. A control add-in is declared in AL but renders as an iframe hosting JavaScript and HTML. It communicates back to AL via the &lt;code&gt;Microsoft.Dynamics.NAV.InvokeExtensibilityMethod&lt;/code&gt; API. A concrete example: an ISV building a logistics product might embed a live shipment tracking map inside the Sales Order page using a control add-in, pulling carrier API data and rendering it as an interactive element alongside the native BC fields. The iframe boundary keeps the JavaScript isolated; the AL callback handles any state updates back to the record.&lt;/p&gt;

&lt;p&gt;Microsoft ships BC updates monthly. One practical consequence of the event subscription model is that if Microsoft removes an event between releases, subscriber code fails silently until you test against the next sandbox. Testing each monthly release in a development tenant before it goes live is not optional for customer-facing BC extensions. For the full API authentication flows and endpoint patterns, see our &lt;a href="https://dev.to/blog/microsoft-dynamics-business-central-api-integration-guide-2025"&gt;Business Central API integration guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sage Intacct
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.apideck.com/connectors/sage-intacct" rel="noopener noreferrer"&gt;Sage Intacct&lt;/a&gt; splits its extensibility model into two distinct layers. External integrations use Web Services, which moved from its XML-only API to a REST API in general availability as of Intacct's 2025 Release 1 (February 2025). For extensions that live inside the Intacct interface, the tool is Platform Services.&lt;/p&gt;

&lt;p&gt;Platform Services lets developers create custom objects (with their own fields and relationships) and build full applications with menus and workflows. Behavior can be injected via JavaScript using the AJAX SDK. A Platform application groups custom objects and navigation menus into a self-contained mini-app inside Intacct. The entire application definition exports as XML, making it portable across Intacct companies.&lt;/p&gt;

&lt;p&gt;A practical example: a professional services firm might use Platform Services to build a project milestone tracker. The developer creates a custom object that links to Intacct's native Project and AP Bill objects. When a milestone is approved, a trigger fires an API call to create a vendor bill. The forms and approval workflow live entirely within Platform Services; the bill creation uses merge fields to pull milestone data into the standard AP Bill form.&lt;/p&gt;

&lt;p&gt;The AJAX SDK has a notable constraint: Sage Intacct explicitly warns against manipulating the standard Intacct DOM (hiding fields with jQuery, for example). Any UI behavior built against undocumented internal elements breaks when Intacct updates its interface. The AJAX Gateway is the only supported path for page scripts that need to call Intacct's back end. This is a narrower surface than many developers expect going in.&lt;/p&gt;

&lt;p&gt;Sage also announced in November 2025 that third-party AI agents can now be embedded in the Sage Copilot interface, with an MCP server built on top of the Intacct REST API. That opens a different category of extension that doesn't require Platform Services at all and may be the easier path for read-heavy use cases.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/2Or3K16JUteb8T9KfmmRgE/dc11664dc7261487b21ffb0364785476/Screenshot_2026-04-22_at_23.28.13.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/2Or3K16JUteb8T9KfmmRgE/dc11664dc7261487b21ffb0364785476/Screenshot_2026-04-22_at_23.28.13.png" alt="Sage Embedded Platform Services"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Acumatica
&lt;/h2&gt;

&lt;p&gt;Acumatica's customization model gives developers more direct access to the ERP's internals than the other three platforms here. Customizations are written in C# and packaged into Customization Projects, which can be exported and deployed across environments. The Customization Project Editor in the Acumatica UI handles screen modifications alongside the C# code.&lt;/p&gt;

&lt;p&gt;The pattern: Acumatica's data objects are declared as DAC (Data Access Class) classes in C#. Business logic lives in Graph classes. An extension subclasses the relevant DAC or Graph, adding or overriding fields and methods without touching the base code. To add an insurance expiration date to the Vendor screen, a developer creates a &lt;code&gt;VendorExtension : PXCacheExtension&amp;lt;Vendor&amp;gt;&lt;/code&gt; class for the field, and a &lt;code&gt;VendorMaintExtension : PXGraphExtension&amp;lt;VendorMaint&amp;gt;&lt;/code&gt; for validation logic. The Customization Project wraps both for deployment.&lt;/p&gt;

&lt;p&gt;For UI, Acumatica shipped a finalized Modern UI in TypeScript and HTML as of 2025 R2 (released September 2025). Extensions to existing Acumatica screens now require creating matching &lt;code&gt;.ts&lt;/code&gt; and &lt;code&gt;.html&lt;/code&gt; files in the &lt;code&gt;extensions&lt;/code&gt; folder under the screen directory. As of 2025 R2, screen personalizations can be included directly in Customization Projects, which lets ISVs guarantee consistent layouts across environments during deployment.&lt;/p&gt;

&lt;p&gt;A concrete example from an ISV: a construction SaaS company might add a compliance status panel to the Subcontract screen, pulling insurance and license data from an external API. The Graph extension handles the data fetch on screen load, while the TypeScript file renders the panel in the Modern UI. The Customization Project packages both for deployment to client tenants.&lt;/p&gt;

&lt;p&gt;One thing to plan for: as of late 2025, ISVs with multiple Customization Projects targeting the same screen have run into conflicts. Acumatica's export-to-development function overwrites files from the development folder, which affects teams managing several products for the same customer. This isn't a blocker, but it requires explicit project management.&lt;/p&gt;

&lt;h2&gt;
  
  
  SAP
&lt;/h2&gt;

&lt;p&gt;SAP sells two products under the same brand that have almost nothing in common from an extensibility standpoint: Business One (B1), aimed at SMBs, and S/4HANA, aimed at mid-market and enterprise. The toolchain, deployment model, and API surface differ enough that treating them as the same platform is a common mistake for ISVs trying to cover both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SAP Business One&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;B1's extension model uses the SDK, which exposes two APIs for different contexts. The DI API (Data Interface API) is a COM-based interface that runs on the B1 client or application server, giving add-ons full access to business objects. The Service Layer is a REST API that runs on HANA-based B1 deployments (9.1 PL09 and later) and is the recommended path for web-based or multi-threaded integrations that don't require the full depth of the DI API. For new integrations, Service Layer is generally simpler; DI API remains necessary when you need objects the Service Layer doesn't yet expose.&lt;/p&gt;

&lt;p&gt;For UI extensions inside the B1 web client specifically, SAP added a UI API extension framework using JavaScript, TypeScript, and SAPUI5. Development happens in VS Code with a dedicated plugin and a Chrome/Edge-based Web Client Inspector for debugging. A UI API extension targets a specific view in the web client (defined by a JSON layout file) and uses a JavaScript controller for logic. A concrete example: an ISV might add a custom panel to the Business Partner Detail view that surfaces a customer's credit insurance status from an external provider. The JSON file positions the new UI element; the controller fetches and displays the data when the panel loads.&lt;/p&gt;

&lt;p&gt;B1 does not use SAP BTP in the same way S/4HANA does. BTP integration is possible but not the primary extensibility model for B1 partners. Most B1 ISV add-ons still ship as traditional SDK-based packages installed on the B1 server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SAP S/4HANA&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;S/4HANA's extensibility model follows SAP's clean core framework, which separates on-stack from side-by-side extensions. On-stack extensions run within S/4HANA itself, written in ABAP Cloud using only publicly released APIs. Side-by-side extensions run on SAP BTP and communicate with S/4HANA through APIs and events.&lt;/p&gt;

&lt;p&gt;SAP formalizes this in a four-level compliance model (A through D). Level A extensions use only released, stable APIs on both paths. Level C and D extensions access internal objects and carry explicit upgrade risk. For ISVs building multi-tenant SaaS on S/4HANA, SAP's August 2025 extensibility documentation positions BTP side-by-side as the preferred architecture: it keeps the extension deployment decoupled from the ERP core, so the ISV manages its own release cadence without waiting on SAP upgrade windows.&lt;/p&gt;

&lt;p&gt;For UI work on S/4HANA, the options split by deployment model. Key-user extensibility lets non-developers add custom fields to standard Fiori apps without code. Developer extensibility goes further with SAPUI5 adaptation projects built in SAP Business Application Studio, which overlay changes onto existing Fiori apps without modifying their base code. New standalone apps can be built with SAPUI5/Fiori Elements and hosted either on-stack in the Fiori Launchpad or on BTP.&lt;/p&gt;

&lt;p&gt;A concrete example for S/4HANA: a manufacturing ISV might build a quality inspection panel embedded alongside the standard Production Order screen. The panel pulls sensor data from an IoT system via a BTP integration, renders a Fiori-based status indicator in the Production Order screen, and writes inspection results back to S/4HANA through an OData service. The application runs on BTP Cloud Foundry, connects to S/4HANA through a communication arrangement, and is distributed to customers through the SAP Store.&lt;/p&gt;

&lt;p&gt;The practical difference between B1 and S/4HANA for an ISV covering both: B1 extensions typically ship as on-premise or hosted SDK packages, while S/4HANA extensions for cloud deployments ship as BTP applications certified through the SAP Store. Different distribution model, different technical stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  What breaks
&lt;/h2&gt;

&lt;p&gt;Each platform has its characteristic failure mode.&lt;/p&gt;

&lt;p&gt;In SAP, the failure mode depends on which product you're targeting. For B1, the DI API's COM dependency limits add-ons to Windows and requires the B1 client to be running, which creates problems in web-only or headless integration scenarios. The Service Layer is cleaner but doesn't cover all DI API objects, so gaps force a context switch back to the older interface mid-project. For S/4HANA, the risk is API scope: if an extension needs access to an internal object not covered by a released API, it falls into Level C or D on the clean core model and carries explicit upgrade risk when SAP ships updates.&lt;/p&gt;

&lt;p&gt;In Business Central, the risk is event volatility. Microsoft adds and deprecates AL events as the base application evolves across monthly releases. A subscriber function becomes a runtime error if the event it depends on is removed. This makes continuous testing against the next monthly release non-negotiable for any BC extension in active customer use.&lt;/p&gt;

&lt;p&gt;In Sage Intacct, the constraint is scope. Platform Services handles custom objects well, but complex UI injection hits the sandbox limits quickly. Scripts that manipulate the standard DOM break on Intacct's quarterly releases without warning, since those changes are outside the supported API surface.&lt;/p&gt;

&lt;p&gt;In Acumatica, the Modern UI transition introduced a new class of migration work. ISVs with ASPX-based screen customizations are rebuilding them in TypeScript for the Modern UI. The new extension file structure requires precision, and multi-project conflicts on shared screens require coordination that didn't exist in the older model.&lt;/p&gt;

&lt;p&gt;In NetSuite, governor limits are the consistent friction point. The script execution time cap hits Suitelets that make external API calls with variable latency. Scheduled scripts queue behind other jobs in the account. These limits are fixed and don't scale with a customer's transaction volume.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "embedded" means at the API layer
&lt;/h2&gt;

&lt;p&gt;The phrase "embedded app" has two meanings that frequently get conflated. In the ERP vendor context, an embedded app is one that runs within the ERP's infrastructure and is distributed through its marketplace. In the SaaS product context, an embedded app is a product experience that lives inside a customer's workflow without requiring them to leave your tool.&lt;/p&gt;

&lt;p&gt;Both definitions are valid. They address different problems. The ERP vendor meaning matters when you're building for a single ERP and care about the vendor's distribution channel. The SaaS product meaning matters when you're building across ERPs and want your product to function consistently regardless of which ERP the customer runs.&lt;/p&gt;

&lt;p&gt;SAP's August 2025 extensibility documentation describes side-by-side on BTP as the preferred path for ISV partners building multi-tenant SaaS, precisely because it decouples deployment and lets the ISV manage its own release cadence. That matches how most SaaS companies want to operate: own the infrastructure and release schedule, treating the ERP as a data source rather than a runtime environment.&lt;/p&gt;

&lt;p&gt;Most extension projects eventually hit a point where the two definitions need to coexist. A vertical SaaS product might embed a lightweight UI extension inside NetSuite to surface its own data in context, while the bulk of data integration runs through a normalized API layer that also handles the customer's other accounting systems. The extension provides presence inside the ERP; the API layer provides breadth. Which of those two problems you need to solve first is the decision worth making explicitly before you pick a toolchain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building for multiple ERPs
&lt;/h2&gt;

&lt;p&gt;For internal IT teams configuring a single ERP instance, the per-platform toolchain is the only path. For SaaS companies whose product needs to work across these four ERPs, maintaining native extensions per platform becomes an engineering capacity problem. No two of these platforms share a development language or deployment model.&lt;/p&gt;

&lt;p&gt;The practical alternative is to build your product's interface outside those frameworks and use APIs for data exchange. Your product handles its own workflows; the ERP handles ERP-native work. For operations that need normalized access to financial data (invoices, customers, and journal entries) across these platforms, a &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;unified accounting API&lt;/a&gt; layer collapses the per-platform API work into a single schema.&lt;/p&gt;

&lt;p&gt;Native embedded extensions still have a clear role. When your product genuinely needs to live inside the ERP's UI, building a native extension for that specific ERP is the right call. The decision is about scope: if "embedded" means your UI inside a single ERP, native extensions make sense. If "embedded" means your product functioning consistently regardless of which accounting system the customer uses, a &lt;a href="https://www.apideck.com/erp-api" rel="noopener noreferrer"&gt;normalized API layer&lt;/a&gt; is cheaper to maintain long-term.&lt;/p&gt;

&lt;p&gt;Most ISV teams end up with a hybrid: lightweight native extensions for presence inside one or two priority ERPs, and a &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;unified API integration&lt;/a&gt; for data sync across the rest.&lt;/p&gt;

</description>
      <category>api</category>
      <category>erp</category>
      <category>unifiedapi</category>
    </item>
    <item>
      <title>How to Get Your Zoho Books API Key</title>
      <dc:creator>Kate Apideck</dc:creator>
      <pubDate>Thu, 06 Aug 2026 14:15:45 +0000</pubDate>
      <link>https://dev.to/apideck/how-to-get-your-zoho-books-api-key-4ell</link>
      <guid>https://dev.to/apideck/how-to-get-your-zoho-books-api-key-4ell</guid>
      <description>&lt;p&gt;Get your Zoho Books OAuth credentials set up and make your first authenticated API call. This guide covers app registration, client types, scopes, the authorization flow, token management, and the mistakes that cost developers time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Zoho Books?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.apideck.com/integrations/zoho-books" rel="noopener noreferrer"&gt;Zoho Books&lt;/a&gt; is Zoho's cloud accounting platform, and it has found its audience in small and mid-sized businesses already inside the Zoho ecosystem. Companies using Zoho CRM, Zoho Projects, or Zoho Inventory that want their accounting in the same stack. It handles invoices, bills, expenses, purchase orders, bank feeds, and tax compliance across a range of countries.&lt;/p&gt;

&lt;p&gt;The API is REST-based, returns JSON, and follows familiar patterns. Version 3 is the current release. Every request requires two things: a valid OAuth 2.0 access token and an &lt;code&gt;organization_id&lt;/code&gt; parameter. Zoho Books supports multiple organizations under one account, and the API enforces strict separation between them. Before you can touch any other endpoint, you'll need the organization ID from &lt;code&gt;GET /organizations&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Zoho Books doesn't use static API keys. Authentication is handled through OAuth 2.0, which means you'll be working with a Client ID, Client Secret, access tokens, and refresh tokens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're planning to connect Zoho Books to your platform alongside other accounting tools, take a look at Apideck's guide on &lt;a href="https://www.apideck.com/blog/how-to-integrate-with-the-zoho-books-api" rel="noopener noreferrer"&gt;how to integrate with the Zoho Books API&lt;/a&gt; for the fuller picture on data mapping and sync patterns. For a broader view of how accounting APIs compare, the &lt;a href="https://www.apideck.com/blog/top-15-accounting-apis-to-integrate-with" rel="noopener noreferrer"&gt;top accounting APIs guide&lt;/a&gt; covers Zoho Books alongside the other major platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A Zoho Books account (any paid plan, or a free trial)&lt;/li&gt;
&lt;li&gt;A Zoho account login to access the Developer Console&lt;/li&gt;
&lt;li&gt;A redirect URI ready for your application (or use the Self Client option if you're building a server-side script without a public URL)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Go to the Zoho Developer Console
&lt;/h2&gt;

&lt;p&gt;Open &lt;a href="https://api-console.zoho.com" rel="noopener noreferrer"&gt;https://api-console.zoho.com&lt;/a&gt; and log in with your Zoho account credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create a New Client
&lt;/h2&gt;

&lt;p&gt;Click &lt;strong&gt;GET STARTED&lt;/strong&gt; (or &lt;strong&gt;ADD CLIENT&lt;/strong&gt; if you've been here before). You'll be asked to choose a client type. The five options are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server-based Applications&lt;/strong&gt;: web apps running on a dedicated server with a redirect URI. This is the right choice for most SaaS integrations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-based Applications&lt;/strong&gt;: browser-only apps (JavaScript, no web server).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile Applications&lt;/strong&gt;: iOS/Android apps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-browser Applications&lt;/strong&gt;: devices without browser support such as smart TVs and printers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self Client&lt;/strong&gt;: standalone server-side scripts or back-end jobs with no redirect URL. Good for testing, internal tooling, or one-off data syncs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a production integration, pick &lt;strong&gt;Server-based Applications&lt;/strong&gt;. If you're just getting credentials to test an endpoint, &lt;strong&gt;Self Client&lt;/strong&gt; is faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Fill in Your Application Details
&lt;/h2&gt;

&lt;p&gt;Once you click &lt;strong&gt;Create Now&lt;/strong&gt; for your chosen client type, fill in the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client Name&lt;/strong&gt;: the name of your application. Zoho uses this on the consent screen your users see.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Homepage URL&lt;/strong&gt;: the full URL of your app's homepage (required for server-based apps).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorized Redirect URIs&lt;/strong&gt;: where Zoho sends the authorization code after the user approves access. Use &lt;code&gt;https://&lt;/code&gt; in production. You can add multiple URIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;CREATE&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Copy Your Client ID and Client Secret
&lt;/h2&gt;

&lt;p&gt;After creation, your &lt;strong&gt;Client ID&lt;/strong&gt; and &lt;strong&gt;Client Secret&lt;/strong&gt; appear in the &lt;strong&gt;Client Secret&lt;/strong&gt; tab. Copy both and store them securely.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client ID&lt;/strong&gt;: public identifier for your app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client Secret&lt;/strong&gt;: keep this private. Don't put it in client-side code, public repos, or browser logs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5: Choose Your Scopes
&lt;/h2&gt;

&lt;p&gt;Scopes control what your app can read and write. Zoho Books uses a consistent naming pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ZohoBooks.{module}.{permission}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where permission is &lt;code&gt;CREATE&lt;/code&gt;, &lt;code&gt;READ&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, or &lt;code&gt;ALL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Common modules and their scopes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;Example Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Invoices&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ZohoBooks.invoices.READ&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contacts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ZohoBooks.contacts.ALL&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bills&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ZohoBooks.bills.CREATE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expenses&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ZohoBooks.expenses.READ&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Banking&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ZohoBooks.banking.READ&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Settings&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ZohoBooks.settings.READ&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accountants&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ZohoBooks.accountants.READ&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Request the minimum scopes your integration needs. Users see these on the consent screen, and a long list raises friction. &lt;code&gt;ZohoBooks.fullaccess.all&lt;/code&gt; exists, but it's overkill for most integrations and looks alarming to end users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Build the Authorization URL
&lt;/h2&gt;

&lt;p&gt;For server-based apps, you'll redirect users to Zoho's authorization endpoint to get a grant token (authorization code). Construct a &lt;code&gt;GET&lt;/code&gt; request to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://accounts.zoho.com/oauth/v2/auth
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these parameters:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;client_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your Client ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;response_type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;code&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;redirect_uri&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One of your registered redirect URIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scope&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Comma-separated scopes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;access_type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;offline&lt;/code&gt; (to receive a refresh token)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A full example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://accounts.zoho.com/oauth/v2/auth?client_id=1000.YOUR_CLIENT_ID&amp;amp;response_type=code&amp;amp;redirect_uri=https://yourapp.com/callback&amp;amp;scope=ZohoBooks.invoices.CREATE,ZohoBooks.contacts.READ&amp;amp;access_type=offline
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the user approves, Zoho redirects to your callback URL with a &lt;code&gt;code&lt;/code&gt; parameter. That's your authorization code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Region note:&lt;/strong&gt; If your Zoho Books account is on the EU data center, use &lt;code&gt;accounts.zoho.eu&lt;/code&gt; instead of &lt;code&gt;accounts.zoho.com&lt;/code&gt;. Same for &lt;code&gt;zoho.in&lt;/code&gt;, &lt;code&gt;zoho.com.au&lt;/code&gt;, &lt;code&gt;zoho.jp&lt;/code&gt;, &lt;code&gt;zoho.ca&lt;/code&gt;, &lt;code&gt;zoho.com.cn&lt;/code&gt;, &lt;code&gt;zoho.sa&lt;/code&gt;, and other regional domains. The authorization URL must match the domain of your user's account.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 7: Exchange the Code for Tokens
&lt;/h2&gt;

&lt;p&gt;Hit Zoho's token endpoint with a &lt;code&gt;POST&lt;/code&gt; request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://accounts.zoho.com/oauth/v2/token
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Body parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;code&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;AUTHORIZATION_CODE&lt;/span&gt;
&lt;span class="py"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;YOUR_CLIENT_ID&lt;/span&gt;
&lt;span class="py"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;YOUR_CLIENT_SECRET&lt;/span&gt;
&lt;span class="py"&gt;redirect_uri&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://yourapp.com/callback&lt;/span&gt;
&lt;span class="py"&gt;grant_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;authorization_code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1000.abc123..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"refresh_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1000.def456..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"api_domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.zohoapis.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save both tokens. The &lt;code&gt;api_domain&lt;/code&gt; field tells you which regional endpoint to use for subsequent API calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Make Your First API Call
&lt;/h2&gt;

&lt;p&gt;Pass the access token in the &lt;code&gt;Authorization&lt;/code&gt; header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Authorization: Zoho-oauthtoken YOUR_ACCESS_TOKEN
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start by fetching your organization ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s1"&gt;'https://www.zohoapis.com/books/v3/organizations'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Authorization: Zoho-oauthtoken YOUR_ACCESS_TOKEN'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grab the &lt;code&gt;organization_id&lt;/code&gt; from the response. You'll need to append &lt;code&gt;?organization_id=YOUR_ORG_ID&lt;/code&gt; to every subsequent request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9: Handle Token Refresh
&lt;/h2&gt;

&lt;p&gt;Access tokens expire after one hour. When a call returns a &lt;code&gt;401&lt;/code&gt;, use your refresh token to get a new one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'https://accounts.zoho.com/oauth/v2/token'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'client_id=YOUR_CLIENT_ID'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'client_secret=YOUR_CLIENT_SECRET'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'refresh_token=YOUR_REFRESH_TOKEN'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'grant_type=refresh_token'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response gives you a new access token. The refresh token stays the same in this flow. If you lose the refresh token, you'll need to restart the OAuth flow from the authorization URL step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self Client: The Shortcut for Server-Side Scripts
&lt;/h2&gt;

&lt;p&gt;If you picked &lt;strong&gt;Self Client&lt;/strong&gt; in Step 2, the flow is different. You don't build an authorization URL or handle redirects. Instead:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On the &lt;strong&gt;Client Secret&lt;/strong&gt; tab, copy your Client ID and Client Secret.&lt;/li&gt;
&lt;li&gt;Switch to the &lt;strong&gt;Generate Code&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Enter your required scopes (comma-separated), pick a time duration for the grant token, add a description, and click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Copy the generated grant token immediately. By default it expires after three minutes, though you can select a longer duration from the dropdown.&lt;/li&gt;
&lt;li&gt;Exchange that code for tokens using the same token endpoint from Step 7.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Self Client works well for background jobs, internal tooling, and testing. It's not suitable for multi-user apps where each customer needs to authorize your app against their own Zoho Books organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Wrong regional domain.&lt;/strong&gt; Zoho Books is multi-datacenter. If a user's account is on &lt;code&gt;zoho.eu&lt;/code&gt;, you need &lt;code&gt;accounts.zoho.eu&lt;/code&gt; for auth and &lt;code&gt;www.zohoapis.eu&lt;/code&gt; for API calls. You can identify the region from the &lt;code&gt;location&lt;/code&gt; parameter returned in Zoho's redirect response after authorization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grant token already used or expired.&lt;/strong&gt; Authorization codes are single-use. For server-based apps, the grant token expires after two minutes. For Self Client, the default is three minutes (though you can extend this when generating the token). If you try to exchange one that's been used or has expired, you'll get &lt;code&gt;invalid_code&lt;/code&gt;. Generate a fresh code and exchange it right away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Missing organization_id.&lt;/strong&gt; Every API endpoint requires &lt;code&gt;?organization_id=YOUR_ORG_ID&lt;/code&gt;. Leaving it out returns a 400 error. Call &lt;code&gt;GET /organizations&lt;/code&gt; first and cache the value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redirect URI mismatch.&lt;/strong&gt; The redirect URI in your authorization request must exactly match one registered in the Developer Console. A trailing slash difference, &lt;code&gt;http&lt;/code&gt; vs &lt;code&gt;https&lt;/code&gt;, or a subdomain variation will return an error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No refresh token.&lt;/strong&gt; If you called the authorization endpoint with &lt;code&gt;access_type=online&lt;/code&gt; (or omitted it, since online is the default), you won't receive a refresh token. Once the access token expires, users have to re-authorize. For production integrations, always use &lt;code&gt;access_type=offline&lt;/code&gt;. If you need a new refresh token after the first authorization, add &lt;code&gt;prompt=consent&lt;/code&gt; to force the consent screen again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate Limits
&lt;/h2&gt;

&lt;p&gt;Zoho Books has some of the tighter rate limits among major accounting APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Per-minute:&lt;/strong&gt; 100 requests per minute per organization (error code 44 on breach)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-day:&lt;/strong&gt; varies by plan

&lt;ul&gt;
&lt;li&gt;Free: 1,000 requests/day&lt;/li&gt;
&lt;li&gt;Standard: 2,000 requests/day&lt;/li&gt;
&lt;li&gt;Professional: 5,000 requests/day&lt;/li&gt;
&lt;li&gt;Premium, Elite, and Ultimate: 10,000 requests/day&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent:&lt;/strong&gt; 5 concurrent calls for Free plans, 10 concurrent calls (soft limit) for paid plans (error code 1070 when exceeded)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The daily limit is shared across all integrations connected to the same organization. If your customer uses other Zoho-connected tools, they're drawing from the same pool. Build exponential backoff into your 429 handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting Through Apideck
&lt;/h2&gt;

&lt;p&gt;If Zoho Books is one of several accounting platforms you need to support, building a separate direct integration for each one gets expensive quickly. Apideck's &lt;a href="https://www.apideck.com/integrations/zoho-books" rel="noopener noreferrer"&gt;Zoho Books connector&lt;/a&gt; connects through a unified accounting API alongside QuickBooks, Xero, Sage, NetSuite, and 30+ other platforms. Authentication, token refresh, rate limit handling, and data normalization are managed at the platform level.&lt;/p&gt;

&lt;p&gt;To configure the connector, open the Apideck dashboard, go to &lt;strong&gt;Configuration &amp;gt; Connectors&lt;/strong&gt;, and select &lt;strong&gt;Zoho Books&lt;/strong&gt; under the Accounting API. You'll see two credential options: use Apideck's shared credentials for quick testing, or enter your own Client ID and Client Secret from the steps above for production use.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/5tNpoE44mVZqkwNkGkVaCJ/a50a5cd09266bfb6effd28aa7dd0f1b0/297_1x_shots_so.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/5tNpoE44mVZqkwNkGkVaCJ/a50a5cd09266bfb6effd28aa7dd0f1b0/297_1x_shots_so.png" alt="How to Get Your Zoho Books API Key 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you've entered your credentials and saved, click &lt;strong&gt;Authorize&lt;/strong&gt; to kick off the OAuth flow. After your users connect their Zoho Books account, the Organization ID field populates automatically.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/1Ux8uZ2YLmFAZo33aGRtg2/a1a9b07bbe76147b55f74a271f6010ce/657_1x_shots_so.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/1Ux8uZ2YLmFAZo33aGRtg2/a1a9b07bbe76147b55f74a271f6010ce/657_1x_shots_so.png" alt="How to Get Your Zoho Books API Key 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The scopes panel lets you control exactly which Zoho Books resources your connector can access. You can select individual scopes per module (contacts, invoices, bills, and so on) rather than granting full access. The Virtual Webhooks section beneath it shows which resources Apideck will poll for updates, with a default 24-hour sync cycle that you can adjust per resource.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/30S3fNRfzhABSkFARxNAf7/6baa30db34bc54c65297013d02d2e312/596_1x_shots_so.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/30S3fNRfzhABSkFARxNAf7/6baa30db34bc54c65297013d02d2e312/596_1x_shots_so.png" alt="How to Get Your Zoho Books API Key 3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ready to connect to Zoho Books and 30+ other accounting platforms through a single API? &lt;a href="https://www.apideck.com/signup" rel="noopener noreferrer"&gt;Get started for free&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>unifiedapi</category>
      <category>zohobooks</category>
      <category>accountingapi</category>
    </item>
    <item>
      <title>How to integrate with the FreeAgent API</title>
      <dc:creator>Kate Apideck</dc:creator>
      <pubDate>Tue, 04 Aug 2026 09:19:38 +0000</pubDate>
      <link>https://dev.to/apideck/how-to-integrate-with-the-freeagent-api-1an6</link>
      <guid>https://dev.to/apideck/how-to-integrate-with-the-freeagent-api-1an6</guid>
      <description>&lt;p&gt;FreeAgent is one of the most popular accounting platforms in the United Kingdom. It serves freelancers and small businesses with invoicing, expense tracking, Self Assessment tax returns, payroll, and MTD-compatible VAT filing. NatWest Group acquired the company in 2018, and it remains a common integration target for software that touches UK financial data.&lt;/p&gt;

&lt;p&gt;This guide covers what you need to know to integrate with the FreeAgent API, from authentication and available endpoints to rate limits, pagination, and the specific challenges that make this integration more involved than the documentation suggests. If you want to skip the complexity of building a direct integration, &lt;a href="https://www.apideck.com/integrations/freeagent" rel="noopener noreferrer"&gt;Apideck's FreeAgent connector&lt;/a&gt; handles authentication, token refresh, and data normalization through a unified Accounting API.&lt;/p&gt;

&lt;h2&gt;
  
  
  What FreeAgent covers
&lt;/h2&gt;

&lt;p&gt;FreeAgent targets a specific segment of the UK market: self-employed professionals, freelancers, and small limited companies. The platform handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Invoicing and estimates&lt;/li&gt;
&lt;li&gt;Expense claims and receipt capture&lt;/li&gt;
&lt;li&gt;Bank account connections and reconciliation&lt;/li&gt;
&lt;li&gt;Self Assessment tax return filing&lt;/li&gt;
&lt;li&gt;VAT returns with Making Tax Digital compliance&lt;/li&gt;
&lt;li&gt;Payroll for small teams&lt;/li&gt;
&lt;li&gt;Project and time tracking&lt;/li&gt;
&lt;li&gt;Multi-currency support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The platform's tight focus on UK tax requirements makes it essential for software serving British freelancers and small businesses. If your customers use FreeAgent, they expect their data to flow into your application without manual exports.&lt;/p&gt;

&lt;h2&gt;
  
  
  API overview
&lt;/h2&gt;

&lt;p&gt;FreeAgent offers two separate API products, and this distinction matters for how you architect your integration.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Company API&lt;/strong&gt; is for direct FreeAgent users: freelancers and small businesses managing their own accounting. This is the standard integration path for most applications.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Accountancy Practice API&lt;/strong&gt; is for accounting firms and bookkeepers who manage multiple client accounts through FreeAgent's Practice Dashboard. This requires a separate app registration, a different OAuth flow, and client-specific subdomains in request headers.&lt;/p&gt;

&lt;p&gt;For a SaaS product that serves both direct business users and accounting firms, this means building and maintaining two distinct integration paths.&lt;/p&gt;

&lt;p&gt;Both APIs are REST-based and support JSON and XML responses. The base URLs are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production: &lt;code&gt;https://api.freeagent.com/v2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sandbox: &lt;code&gt;https://api.sandbox.freeagent.com/v2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sandbox environment lets you test your integration without affecting live data. FreeAgent provides temporary sandbox accounts through their developer portal for this purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication
&lt;/h2&gt;

&lt;p&gt;FreeAgent uses OAuth 2.0 with the authorization code flow. To get started:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an account at the &lt;a href="https://dev.freeagent.com/" rel="noopener noreferrer"&gt;FreeAgent Developer Dashboard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Register a new application to receive your OAuth Client ID and Client Secret&lt;/li&gt;
&lt;li&gt;Specify which access level your application requires (more on this below)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The authorization endpoint is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.freeagent.com/v2/approve_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The token endpoint is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.freeagent.com/v2/token_endpoint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A basic token exchange looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.freeagent.com/v2/token_endpoint &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"client_id=YOUR_CLIENT_ID"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"client_secret=YOUR_CLIENT_SECRET"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"grant_type=authorization_code"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"code=AUTH_CODE"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"redirect_uri=YOUR_REDIRECT_URI"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access tokens expire after one hour. Refresh tokens are long-lived (approximately 20 years according to the API response), so you can maintain persistent access without requiring users to re-authenticate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access levels
&lt;/h3&gt;

&lt;p&gt;FreeAgent uses a numeric access level system (0 through 8) that controls which resources your app can read or write. When you register your application, you need to specify the access level you require. Each API resource has a minimum required level, and requesting a higher level than necessary will likely slow down your app approval.&lt;/p&gt;

&lt;p&gt;Review the documentation to determine the minimum access level that covers your use case. If you only need to read invoices and contacts, you don't need the same permissions as an app that manages payroll.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key endpoints
&lt;/h2&gt;

&lt;p&gt;The Company API covers the core accounting resources you'd expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/v2/contacts&lt;/code&gt; for customers and suppliers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v2/invoices&lt;/code&gt; for sales invoices&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v2/bills&lt;/code&gt; for purchase invoices&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v2/expenses&lt;/code&gt; for expense claims&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v2/bank_accounts&lt;/code&gt; for connected bank accounts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v2/bank_transactions&lt;/code&gt; for bank transaction data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v2/projects&lt;/code&gt; for project management&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v2/tasks&lt;/code&gt; for time tracking&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v2/users&lt;/code&gt; for user accounts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v2/company&lt;/code&gt; for company settings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v2/categories&lt;/code&gt; for expense categories&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v2/tax_rates&lt;/code&gt; for VAT rates&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v2/journal_entries&lt;/code&gt; for manual journal entries&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v2/payroll_profiles&lt;/code&gt; and &lt;code&gt;/v2/payslips&lt;/code&gt; for payroll data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API supports standard CRUD operations on most resources. Each endpoint is documented in the &lt;a href="https://dev.freeagent.com/docs" rel="noopener noreferrer"&gt;FreeAgent API documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate limits
&lt;/h2&gt;

&lt;p&gt;FreeAgent enforces the following rate limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;120 requests per minute per user&lt;/li&gt;
&lt;li&gt;3,600 requests per hour per user&lt;/li&gt;
&lt;li&gt;15 token refreshes per minute per user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These limits reset at the start of every hour or minute. For Accountancy Practice API users, rate limits apply per client subdomain rather than to the practice as a whole.&lt;/p&gt;

&lt;p&gt;When you hit a rate limit, the API returns a 429 response with a &lt;code&gt;Retry-After&lt;/code&gt; header indicating how long to wait:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;429&lt;/span&gt; &lt;span class="ne"&gt;Too Many Requests&lt;/span&gt;
&lt;span class="na"&gt;Retry-After&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;60&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 120 requests per minute limit is generous for most use cases. However, the 15 token refreshes per minute limit creates a specific bottleneck for multi-tenant applications. If you're running a SaaS product with many customers and you trigger concurrent token refreshes during a scheduled sync, you can exhaust this limit before pulling any actual data. You'll need to implement token refresh queuing or stagger sync schedules across your customer base.&lt;/p&gt;

&lt;p&gt;FreeAgent provides a testing header &lt;code&gt;X-RateLimit-Test&lt;/code&gt; that artificially lowers the sandbox rate limit to 5 requests per minute, letting you verify your backoff strategy before hitting production limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pagination
&lt;/h2&gt;

&lt;p&gt;API requests that return multiple items are paginated with a default of 25 items per page and a maximum of 100 items per page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://api.freeagent.com/v2/invoices?page=2&amp;amp;per_page=50
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pagination metadata comes in headers rather than the response body:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Link&lt;/code&gt; header contains URLs for &lt;code&gt;prev&lt;/code&gt;, &lt;code&gt;next&lt;/code&gt;, &lt;code&gt;first&lt;/code&gt;, and &lt;code&gt;last&lt;/code&gt; pages&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;X-Total-Count&lt;/code&gt; header contains the total number of items&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The 25-item default is lower than most accounting APIs. If you're syncing large datasets, you'll want to set &lt;code&gt;per_page=100&lt;/code&gt; and implement proper pagination handling to avoid missing records.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with contacts
&lt;/h2&gt;

&lt;p&gt;Creating a contact requires minimal data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.freeagent.com/v2/contacts &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_ACCESS_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "contact": {
      "first_name": "Jane",
      "last_name": "Smith",
      "email": "jane@example.com",
      "organisation_name": "Smith Consulting"
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful creation returns a 201 status with the created contact object, including the FreeAgent URL identifier you'll need for subsequent operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration challenges
&lt;/h2&gt;

&lt;p&gt;Beyond the two-API-product architecture and rate limits, several characteristics make FreeAgent integrations more complex than they initially appear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No webhooks.&lt;/strong&gt; The standard FreeAgent API doesn't support webhooks for real-time notifications. You'll need to poll for changes, which means designing an efficient sync strategy that respects rate limits while keeping data reasonably current. This is a meaningful limitation for applications that need near-real-time data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UK-specific data models.&lt;/strong&gt; FreeAgent's data structures reflect UK accounting conventions and tax requirements. VAT handling, Self Assessment categories, and MTD-specific fields may not map cleanly to more generic accounting schemas. If you're building a multi-country application, you'll need transformation logic specific to FreeAgent's UK-centric model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation in context.&lt;/strong&gt; While the API documentation is available through the developer portal, some details only become clear through trial and error. Error messages are generally helpful, but the sandbox environment is essential for understanding how the API actually behaves before you deploy to production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling beyond FreeAgent
&lt;/h2&gt;

&lt;p&gt;FreeAgent covers the UK freelancer and small business market well, but most B2B applications need to integrate with multiple accounting platforms. Your UK customers use FreeAgent, your US customers use QuickBooks, your Dutch customers use SnelStart, and your Australian customers use Xero.&lt;/p&gt;

&lt;p&gt;Building and maintaining direct integrations to each platform means handling different authentication flows, different data models, different rate limits, and different error handling for every connector. Each integration requires its own development time and ongoing maintenance as APIs change.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://www.apideck.com/blog/what-is-a-unified-api" rel="noopener noreferrer"&gt;unified API&lt;/a&gt; gives you one integration point and one data schema across all of them. Instead of managing FreeAgent's token refresh limits, QuickBooks' OAuth flow, and Xero's pagination separately, you write against a single normalized &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;Accounting API&lt;/a&gt; and let the platform handle the connector-specific complexity.&lt;/p&gt;

&lt;p&gt;With Apideck, listing invoices from FreeAgent looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Apideck&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@apideck/node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Apideck&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIDECK_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIDECK_APP_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;consumerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;apideck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accounting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoicesAll&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;serviceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;freeagent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same code structure works for &lt;a href="https://www.apideck.com/blog/how-to-integrate-your-app-with-quickbooks-online" rel="noopener noreferrer"&gt;QuickBooks&lt;/a&gt;, &lt;a href="https://www.apideck.com/blog/how-to-integrate-sage-business-cloud-accounting-api" rel="noopener noreferrer"&gt;Sage&lt;/a&gt;, &lt;a href="https://www.apideck.com/integrations/xero" rel="noopener noreferrer"&gt;Xero&lt;/a&gt;, and every other accounting connector in the platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.apideck.com/signup" rel="noopener noreferrer"&gt;Create a free&lt;/a&gt; Apideck account to get started.&lt;/p&gt;

</description>
      <category>api</category>
      <category>freeagentapi</category>
      <category>accountingapi</category>
      <category>unifiedapi</category>
    </item>
    <item>
      <title>How to Integrate with the Zoho Books API</title>
      <dc:creator>Kate Apideck</dc:creator>
      <pubDate>Mon, 03 Aug 2026 12:27:56 +0000</pubDate>
      <link>https://dev.to/apideck/how-to-integrate-with-the-zoho-books-api-3hl5</link>
      <guid>https://dev.to/apideck/how-to-integrate-with-the-zoho-books-api-3hl5</guid>
      <description>&lt;p&gt;Zoho Books is a cloud-based accounting platform popular with small and mid-sized businesses, particularly those already embedded in the Zoho ecosystem. If your product serves finance teams, you'll eventually get a request to pull invoice data from Zoho Books or push expense records into it.&lt;/p&gt;

&lt;p&gt;This guide covers the technical work involved: authentication, key endpoints, rate limits, data mapping, and the common pitfalls that trip up first-time integrators. If you want to skip the direct integration work, &lt;a href="https://www.apideck.com/integrations/zoho-books" rel="noopener noreferrer"&gt;Apideck's Zoho Books connector&lt;/a&gt; handles the plumbing through a unified accounting API.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Zoho Books API offers
&lt;/h2&gt;

&lt;p&gt;The Zoho Books API is a REST API that returns JSON responses. Version 3 is the current release and covers the full range of accounting operations: contacts, invoices, expenses, bills, bank transactions, purchase orders, and sales orders.&lt;/p&gt;

&lt;p&gt;Every API request requires two things: an OAuth 2.0 access token and an &lt;code&gt;organization_id&lt;/code&gt; parameter. Zoho Books supports multiple organizations under a single account, and the API enforces strict separation between them. You'll need to retrieve the organization ID first (via GET /organizations) before calling any other endpoint.&lt;/p&gt;

&lt;p&gt;Base URL: &lt;code&gt;https://www.zohoapis.com/books/v3/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;One complication: Zoho operates regional data centers with distinct API domains: .com, .eu, .in, .com.au, .jp, .ca, .sa (Saudi Arabia), and .uk. The API domain must match where the user's Zoho Books account is hosted. A customer on zoho.eu needs API calls to zohoapis.eu. Getting this wrong produces authentication errors that look like token problems.&lt;/p&gt;

&lt;p&gt;The modern approach is to detect the data center automatically during OAuth. When you make the initial authorization request to &lt;code&gt;accounts.zoho.com&lt;/code&gt;, the response includes a &lt;code&gt;location&lt;/code&gt; parameter indicating the user's data center. You can also call &lt;code&gt;https://accounts.zoho.com/oauth/serverinfo&lt;/code&gt; to get the full mapping of location codes to API domains. This beats asking users to identify their region manually.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/5yZjOq7s9H3nOgLArolHYU/4ca140d75561c032c1afa5c437d295a2/Screenshot_2026-03-25_at_06.49.31.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/5yZjOq7s9H3nOgLArolHYU/4ca140d75561c032c1afa5c437d295a2/Screenshot_2026-03-25_at_06.49.31.png" alt="Zoho Books API"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication setup
&lt;/h2&gt;

&lt;p&gt;Zoho uses OAuth 2.0 with the authorization code grant type. The flow works like most OAuth implementations, with a few Zoho-specific details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Register your application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to the Zoho Developer Console and create a new client. You'll choose between two client types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Web-based&lt;/strong&gt;: Standard OAuth flow with redirect URIs. Use this for applications where users authenticate through a browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-client&lt;/strong&gt;: Server-to-server authentication. Generates tokens without user interaction after the initial setup. Useful for backend services and scheduled syncs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Registration gives you a client ID and client secret.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implement the authorization flow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For web-based clients, redirect users to Zoho's authorization endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://accounts.zoho.com/oauth/v2/auth
  ?response_type=code
  &amp;amp;client_id={your_client_id}
  &amp;amp;scope=ZohoBooks.invoices.READ,ZohoBooks.contacts.READ
  &amp;amp;redirect_uri={your_redirect_uri}
  &amp;amp;access_type=offline
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;access_type=offline&lt;/code&gt; parameter is important. Without it, you won't receive a refresh token, and your integration will break after an hour when the access token expires.&lt;/p&gt;

&lt;p&gt;After the user authorizes, Zoho redirects to your URI with an authorization code. Exchange it for tokens within two minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://accounts.zoho.com/oauth/v2/token
  ?code={authorization_code}
  &amp;amp;client_id={your_client_id}
  &amp;amp;client_secret={your_client_secret}
  &amp;amp;redirect_uri={your_redirect_uri}
  &amp;amp;grant_type=authorization_code
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Manage token lifecycle&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Access tokens expire after one hour. Refresh tokens don't expire unless revoked, and you can reuse the same refresh token to generate new access tokens repeatedly. Store the refresh token securely and use it to generate new access tokens before they expire:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://accounts.zoho.com/oauth/v2/token
  ?refresh_token={refresh_token}
  &amp;amp;client_id={your_client_id}
  &amp;amp;client_secret={your_client_secret}
  &amp;amp;grant_type=refresh_token
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zoho's scopes are granular. Instead of broad permissions like "accounting:read", you request specific capabilities: &lt;code&gt;ZohoBooks.invoices.CREATE&lt;/code&gt;, &lt;code&gt;ZohoBooks.contacts.READ&lt;/code&gt;, &lt;code&gt;ZohoBooks.expenses.UPDATE&lt;/code&gt;. Request only what you need. The full scope list covers contacts, settings, estimates, invoices, customer payments, credit notes, projects, expenses, sales orders, purchase orders, bills, debit notes, vendor payments, banking, and accountant operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key endpoints
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Common operations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Contacts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/contacts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create and manage customers and vendors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Invoices&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/invoices&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create, send, and track invoices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expenses&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/expenses&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Record and categorize expenses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bills&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/bills&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Manage accounts payable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Banking&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/bankaccounts&lt;/code&gt;, &lt;code&gt;/banktransactions&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Bank feeds and reconciliation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recurring&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/recurringinvoices&lt;/code&gt;, &lt;code&gt;/recurringexpenses&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Subscription billing and repeat expenses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orders&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/salesorder&lt;/code&gt;, &lt;code&gt;/purchaseorder&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Sales and purchase order management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few endpoints worth noting:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;POST /invoices/fromsalesorder&lt;/code&gt; creates an invoice directly from a sales order, which saves a round trip if you're building order-to-cash workflows.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;POST /invoices/email&lt;/code&gt; and &lt;code&gt;POST /invoices/paymentreminder&lt;/code&gt; handle customer communication without leaving the API.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET /organizations&lt;/code&gt; returns all organizations the authenticated user can access, along with currency settings, fiscal year configuration, and time zone. Call this first to get the organization_id you'll need for everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data mapping considerations
&lt;/h2&gt;

&lt;p&gt;Zoho Books uses its own data model, which won't map 1:1 to other accounting systems. If you're building a &lt;a href="https://www.apideck.com/blog/top-15-accounting-apis-to-integrate-with" rel="noopener noreferrer"&gt;multi-platform integration&lt;/a&gt;, here are the fields that cause the most trouble.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contacts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Zoho combines customers and vendors into a single contacts endpoint, distinguished by &lt;code&gt;contact_type&lt;/code&gt;. The &lt;code&gt;contact_name&lt;/code&gt; field has a 200-character limit. Company information goes in &lt;code&gt;company_name&lt;/code&gt;, not the contact name. Billing and shipping addresses are nested objects with their own field structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invoices&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tax handling is line-item based. Each line item can have its own tax rate, and the tax configuration at the header level must match the line items. Mismatches produce validation errors that aren't always obvious from the error message.&lt;/p&gt;

&lt;p&gt;Line items use &lt;code&gt;item_id&lt;/code&gt; for products from your Zoho inventory or can be created as custom items with &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;rate&lt;/code&gt;, and &lt;code&gt;quantity&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom fields&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most resources support custom fields through a &lt;code&gt;custom_fields&lt;/code&gt; array. Each custom field requires a &lt;code&gt;customfield_id&lt;/code&gt; (retrieved from the organization settings API) and a &lt;code&gt;value&lt;/code&gt;. Custom field IDs are organization-specific, so don't hardcode them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate limits
&lt;/h2&gt;

&lt;p&gt;Zoho Books has some of the most restrictive rate limits among major accounting APIs. Plan your integration accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-minute limit&lt;/strong&gt;: 100 requests per minute per organization. Exceeding this returns HTTP 429 with error code 44.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Daily limits&lt;/strong&gt; vary by plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free: 1,000 calls/day&lt;/li&gt;
&lt;li&gt;Standard: 2,000 calls/day&lt;/li&gt;
&lt;li&gt;Professional: 5,000 calls/day&lt;/li&gt;
&lt;li&gt;Premium/Elite/Ultimate: 10,000 calls/day&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Exceeding daily limits returns HTTP 429 with error code 45.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrent limit&lt;/strong&gt;: Roughly 10 simultaneous requests per organization. HTTP 429 with error code 1070 indicates you've hit this ceiling.&lt;/p&gt;

&lt;p&gt;The daily limit is shared across all API consumers for that organization. If your customer uses other Zoho integrations or third-party tools that call the API, those count against the same quota. This creates situations where your integration fails because something else exhausted the limit.&lt;/p&gt;

&lt;p&gt;Build in exponential backoff for 429 responses. For bulk operations, consider batching and spreading requests across longer time windows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error handling
&lt;/h2&gt;

&lt;p&gt;Zoho Books returns HTTP status codes with JSON error bodies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"For security reasons your account has been blocked as you have exceeded the maximum number of requests per minute."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common error codes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt;: Success&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;44&lt;/code&gt;: Per-minute rate limit exceeded&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;45&lt;/code&gt;: Daily rate limit exceeded&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1070&lt;/code&gt;: Concurrent request limit exceeded&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1000&lt;/code&gt;-&lt;code&gt;1099&lt;/code&gt;: Various validation and business logic errors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Log the full error response, not just the HTTP status. The &lt;code&gt;code&lt;/code&gt; field often contains more actionable information than the status code alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing your integration
&lt;/h2&gt;

&lt;p&gt;Zoho Books doesn't offer a dedicated sandbox environment. You have two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a test organization&lt;/strong&gt;: Set up a separate Zoho Books organization for development. This uses real API calls against real infrastructure but keeps test data isolated from production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use a trial account&lt;/strong&gt;: Zoho offers free trials that provide API access. Useful for testing, but trial limitations may not reflect production behavior.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Test these scenarios specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token refresh: Let an access token expire and verify your refresh logic works&lt;/li&gt;
&lt;li&gt;Rate limit handling: Intentionally exceed limits and confirm your backoff implementation&lt;/li&gt;
&lt;li&gt;Multi-organization: If your users might have multiple Zoho Books organizations, test the organization selection flow&lt;/li&gt;
&lt;li&gt;Regional domains: Test with accounts on different Zoho data centers if your customer base is international&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Webhooks and real-time sync
&lt;/h2&gt;

&lt;p&gt;Zoho Books supports outgoing webhooks for real-time notifications. You can configure webhooks in Zoho Books to fire when events occur (invoices created, payments received, contacts updated, etc.), sending HTTP POST requests to your endpoint with the event payload. This eliminates the need for constant polling.&lt;/p&gt;

&lt;p&gt;To set up webhooks, navigate to Settings &amp;gt; Automation &amp;gt; Webhooks in the Zoho Books UI and define the trigger events and destination URL. You can also receive incoming webhooks from external systems to create or update records in Zoho Books.&lt;/p&gt;

&lt;p&gt;For scenarios where webhooks aren't practical, you can poll endpoints periodically using the &lt;code&gt;last_modified_time&lt;/code&gt; field on most resources to filter for recent updates. But webhooks are the cleaner approach for near-real-time sync requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting through a unified API
&lt;/h2&gt;

&lt;p&gt;Building a direct Zoho Books integration makes sense if Zoho is your only target. But most B2B applications need to support multiple accounting platforms. Your QuickBooks customers expect the same sync features as your Zoho customers, and your Xero customers will ask for parity next quarter.&lt;/p&gt;

&lt;p&gt;Direct integrations mean duplicating the authentication, data mapping, error handling, and maintenance work for each platform. A &lt;a href="https://www.apideck.com/blog/what-is-a-unified-api" rel="noopener noreferrer"&gt;unified accounting API&lt;/a&gt; abstracts those differences behind a single interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.apideck.com/integrations/zoho-books" rel="noopener noreferrer"&gt;Apideck's accounting API&lt;/a&gt; normalizes Zoho Books alongside QuickBooks, Xero, Sage, NetSuite, and 45+ other platforms. One integration gives your customers their choice of accounting software. Authentication, webhooks, and rate limit management are handled at the platform level.&lt;/p&gt;

&lt;p&gt;This approach trades some Zoho-specific customization for dramatically reduced integration scope. If you're building &lt;a href="https://www.apideck.com/blog/how-to-integrate-your-app-with-quickbooks-online" rel="noopener noreferrer"&gt;invoice automation&lt;/a&gt; or expense sync features, the normalized data model covers the common operations without requiring platform-specific code paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;If you're building direct to Zoho Books:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Register at the Zoho Developer Console&lt;/li&gt;
&lt;li&gt;Implement OAuth with offline access&lt;/li&gt;
&lt;li&gt;Detect data centers automatically via the &lt;code&gt;location&lt;/code&gt; parameter in OAuth responses&lt;/li&gt;
&lt;li&gt;Store organization IDs and regional domains per customer&lt;/li&gt;
&lt;li&gt;Build rate limit handling from day one&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to support multiple accounting platforms:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://www.apideck.com/signup" rel="noopener noreferrer"&gt;Sign up&lt;/a&gt; for an Apideck account&lt;/li&gt;
&lt;li&gt;Enable the Zoho Books connector&lt;/li&gt;
&lt;li&gt;Use the unified accounting API for contacts, invoices, and expenses&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does Zoho Books have a sandbox environment?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. Test against a separate organization or trial account to avoid polluting production data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I handle customers on different Zoho data centers?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Detect the data center automatically during OAuth. The authorization response includes a &lt;code&gt;location&lt;/code&gt; parameter (e.g., &lt;code&gt;us&lt;/code&gt;, &lt;code&gt;eu&lt;/code&gt;, &lt;code&gt;in&lt;/code&gt;). Use &lt;code&gt;https://accounts.zoho.com/oauth/serverinfo&lt;/code&gt; to map location codes to the correct API domains. Store the data center domain alongside the OAuth tokens for each customer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why am I getting "invalid_code" when exchanging the authorization code?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Authorization codes expire after two minutes and can only be used once. If your token exchange is slow or you retry a failed request with the same code, you'll hit this error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I increase the API rate limits?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The limits are tied to the Zoho Books subscription plan. Your customer would need to upgrade to a higher tier for increased limits. There's no separate API-specific upgrade path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Zoho Books support webhooks?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Zoho Books supports outgoing webhooks that notify your application when events occur. Configure them through Settings &amp;gt; Automation &amp;gt; Webhooks in the Zoho Books interface. You can trigger webhooks on invoice creation, payment receipt, contact updates, and other events.&lt;/p&gt;

</description>
      <category>api</category>
      <category>zohobooksapi</category>
      <category>unifiedapi</category>
      <category>accounting</category>
    </item>
    <item>
      <title>How to Create a Stripe App</title>
      <dc:creator>Kate Apideck</dc:creator>
      <pubDate>Fri, 31 Jul 2026 12:28:23 +0000</pubDate>
      <link>https://dev.to/apideck/how-to-create-a-stripe-app-48mh</link>
      <guid>https://dev.to/apideck/how-to-create-a-stripe-app-48mh</guid>
      <description>&lt;p&gt;Stripe handles payments for millions of businesses, from startups to enterprises. If you're building an integration that syncs financial data from Stripe, you'll need to create a Stripe App to obtain OAuth credentials. This guide walks through creating a Stripe App and retrieving the credentials required to connect Stripe to other platforms.&lt;/p&gt;

&lt;p&gt;For a unified approach to accounting integrations, check out the &lt;a href="https://www.apideck.com/integrations/stripe" rel="noopener noreferrer"&gt;Apideck Stripe connector&lt;/a&gt;, which lets you pull Stripe data alongside 30+ other accounting platforms through a single API.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Stripe?
&lt;/h2&gt;

&lt;p&gt;Stripe is a payment processing platform that enables businesses to accept online payments and manage recurring billing. Beyond payment processing, Stripe exposes financial data through its API, including transactions and customer records. For accounting integrations, this data is typically accessed via Stripe Apps using OAuth rather than raw API keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you start, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://dashboard.stripe.com/register" rel="noopener noreferrer"&gt;Stripe account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;An Apideck account&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed and up to date&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stripe.com/docs/stripe-cli" rel="noopener noreferrer"&gt;Stripe CLI&lt;/a&gt; version 1.12.4 or newer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating a Stripe App
&lt;/h2&gt;

&lt;p&gt;Apideck's Stripe connector uses the Stripe Apps platform for OAuth authentication. For the complete walkthrough, see &lt;a href="https://developers.apideck.com/connectors/stripe/docs/application_owner+oauth_credentials" rel="noopener noreferrer"&gt;Apideck's OAuth Credentials Guide&lt;/a&gt;. Here's a summary of the key steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the &lt;a href="https://stripe.com/docs/stripe-cli" rel="noopener noreferrer"&gt;Stripe CLI&lt;/a&gt; if you haven't already.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;stripe login&lt;/code&gt; to authenticate with your Stripe account.&lt;/li&gt;
&lt;li&gt;Verify your CLI version is 1.12.4 or newer with &lt;code&gt;stripe version&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Install the Stripe Apps plugin: &lt;code&gt;stripe plugin install apps&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Verify the apps plugin version (should be 1.5.12 or later): &lt;code&gt;stripe apps -v&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create a new app: &lt;code&gt;stripe apps create apideck-integration&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Follow the prompts for your app ID and display name.&lt;/li&gt;
&lt;li&gt;Navigate to your app directory: &lt;code&gt;cd apideck-integration&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Upload your app: &lt;code&gt;stripe apps upload&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once uploaded, you can access your app's OAuth credentials in the Stripe Dashboard under &lt;strong&gt;Developers &amp;gt; Created apps&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Your Client ID and Client Secret
&lt;/h2&gt;

&lt;p&gt;After uploading your Stripe App:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Stripe Dashboard, go to &lt;strong&gt;Developers &amp;gt; Created apps&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click your app to open its details page.&lt;/li&gt;
&lt;li&gt;Open the &lt;strong&gt;External test/live&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Your &lt;strong&gt;Client ID&lt;/strong&gt; is displayed here (starts with &lt;code&gt;ca_&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Generate a &lt;strong&gt;Client Secret&lt;/strong&gt; if you haven't already. Store this securely as it won't be shown again.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setting Up the Redirect URI
&lt;/h2&gt;

&lt;p&gt;OAuth requires a redirect URI where users return after authorizing the connection. For Stripe Apps, you configure this in your app manifest file (&lt;code&gt;stripe-app.json&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allowed_redirect_uris"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://unify.apideck.com/vault/callback"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find a complete working example in the &lt;a href="https://github.com/apideck-samples/stripe-app/blob/main/stripe-app.json" rel="noopener noreferrer"&gt;apideck-samples/stripe-app&lt;/a&gt; public repo.&lt;/p&gt;

&lt;p&gt;After updating your manifest, run &lt;code&gt;stripe apps upload&lt;/code&gt; to apply the changes. Without the correct redirect URI, the OAuth flow will fail with an invalid redirect error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding Your Channel Link ID
&lt;/h2&gt;

&lt;p&gt;Apideck's Stripe connector requires a Channel Link ID, which identifies your app's OAuth install link.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Stripe Dashboard, go to &lt;strong&gt;Developers &amp;gt; Created apps&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click your app to open its details page.&lt;/li&gt;
&lt;li&gt;Open the &lt;strong&gt;External test/live&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Locate the OAuth install link in the Test OAuth section.&lt;/li&gt;
&lt;li&gt;The Channel Link ID is part of this URL, formatted as &lt;code&gt;chnlink_xxxxx&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Copy this value for use in the Apideck connector configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Stripe Account IDs
&lt;/h2&gt;

&lt;p&gt;When users connect their Stripe accounts through your integration, they may need to provide their Stripe Account ID. This is different from the OAuth credentials you just created. The Account ID:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Starts with &lt;code&gt;acct_&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Is found in the Stripe Dashboard under &lt;strong&gt;Settings &amp;gt; Account details&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Identifies the specific Stripe account being connected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ID is used by your integration to access bank account information and other account-specific data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Wrong redirect URI&lt;/strong&gt;: The most frequent OAuth error. Double-check that the redirect URI in your Stripe App matches exactly what your integration expects. Trailing slashes matter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mixing test and live credentials&lt;/strong&gt;: Stripe maintains separate credentials for test mode and live mode. If your integration isn't seeing real data, verify you're using live credentials, not test ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Missing permissions&lt;/strong&gt;: If your integration can't access certain data, check whether you've enabled the required permissions in your app manifest. The &lt;code&gt;stripe_apps&lt;/code&gt; scope is used for OAuth authentication, but actual data access depends on the permissions you've defined.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client Secret exposure&lt;/strong&gt;: Never commit your Client Secret to version control or expose it in client-side code. Treat it like a password.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate Limits
&lt;/h2&gt;

&lt;p&gt;Stripe's API uses rate limiting to ensure stability. The default rate limit is 100 operations per second in live mode and 25 operations per second in sandbox mode. Some endpoints have stricter limits. If you hit rate limits, Stripe returns a &lt;code&gt;429 Too Many Requests&lt;/code&gt; response. The &lt;code&gt;Stripe-Rate-Limited-Reason&lt;/code&gt; header indicates which limit was exceeded.&lt;/p&gt;

&lt;p&gt;For high-volume integrations, consider implementing exponential backoff and caching frequently accessed data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting Stripe via Apideck
&lt;/h2&gt;

&lt;p&gt;Apideck's &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;unified Accounting API&lt;/a&gt; lets you connect to &lt;a href="https://www.apideck.com/integrations/stripe" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt; alongside platforms like &lt;a href="https://www.apideck.com/blog/how-to-get-your-quickbooks-api-key" rel="noopener noreferrer"&gt;QuickBooks&lt;/a&gt; and &lt;a href="https://www.apideck.com/blog/how-to-get-xero-api-key" rel="noopener noreferrer"&gt;Xero&lt;/a&gt; through a single integration. Once you have your Stripe App credentials, configuring the connector takes a few minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Admin Configuration
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Log into your Apideck account and navigate to the &lt;strong&gt;Configuration&lt;/strong&gt; page.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Unified APIs&lt;/strong&gt;, select &lt;strong&gt;Accounting&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Find the Stripe connector and click to expand its settings.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/5ML5OXJKhKrQrHeYYUSnGl/8f36e3b1f4838e68b7279753bdca119a/964_1x_shots_so.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/5ML5OXJKhKrQrHeYYUSnGl/8f36e3b1f4838e68b7279753bdca119a/964_1x_shots_so.png" alt="How to Get Your Stripe API Key 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enter your &lt;strong&gt;Client ID&lt;/strong&gt; and &lt;strong&gt;Client Secret&lt;/strong&gt; from your Stripe App.&lt;/li&gt;
&lt;li&gt;Scroll down to the &lt;strong&gt;Scopes&lt;/strong&gt; section and enable &lt;code&gt;stripe_apps&lt;/code&gt; for full access to account data.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;OAuth Configuration&lt;/strong&gt; section, enter your &lt;strong&gt;Channel Link ID&lt;/strong&gt; (format: &lt;code&gt;chnlink_xxxxx&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/YtnoKh7urpjwDUAcxIJtn/68f2aaa56ef6fde3aebe3bb803592413/358_1x_shots_so.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/YtnoKh7urpjwDUAcxIJtn/68f2aaa56ef6fde3aebe3bb803592413/358_1x_shots_so.png" alt="How to Get Your Stripe API Key 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Save settings&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  User Connection Flow
&lt;/h3&gt;

&lt;p&gt;Once admin configuration is complete, your users can connect their Stripe accounts through Apideck Vault:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When a user initiates the connection, they'll see the Stripe authorization modal.&lt;/li&gt;
&lt;li&gt;The user enters their &lt;strong&gt;Stripe Account ID&lt;/strong&gt; (found in their Stripe Dashboard under Settings &amp;gt; Account details).&lt;/li&gt;
&lt;li&gt;They click &lt;strong&gt;Authorize&lt;/strong&gt; to complete the OAuth flow.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/50HD22csaVTGmjx1PtHFMm/91dbcf21079f16892b859fdfd2f3cc38/853_1x_shots_so.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/50HD22csaVTGmjx1PtHFMm/91dbcf21079f16892b859fdfd2f3cc38/853_1x_shots_so.png" alt="How to Get Your Stripe API Key 3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After authorization, you can pull financial data from Stripe through Apideck's unified Accounting API.&lt;/p&gt;

&lt;p&gt;Ready to connect to Stripe and 45+ other accounting platforms through a single API? &lt;a href="https://www.apideck.com/signup" rel="noopener noreferrer"&gt;Get started for free&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>stripe</category>
      <category>unifiedapi</category>
    </item>
    <item>
      <title>How to Get Your Stripe API Key</title>
      <dc:creator>Kate Apideck</dc:creator>
      <pubDate>Wed, 29 Jul 2026 13:55:08 +0000</pubDate>
      <link>https://dev.to/apideck/how-to-get-your-stripe-api-key-3gb6</link>
      <guid>https://dev.to/apideck/how-to-get-your-stripe-api-key-3gb6</guid>
      <description>&lt;p&gt;Stripe provides API keys that let you integrate payment processing, billing, and financial data into your applications. This guide walks through finding and creating API keys in the Stripe Dashboard.&lt;/p&gt;

&lt;p&gt;For a unified approach to accounting integrations, check out the &lt;a href="https://www.apideck.com/integrations/stripe" rel="noopener noreferrer"&gt;Apideck Stripe connector&lt;/a&gt;, which lets you pull Stripe data alongside 30+ other accounting platforms through a single API.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Stripe?
&lt;/h2&gt;

&lt;p&gt;Stripe is a payment processing platform that enables businesses to accept online payments and manage recurring billing. Through its API, you can access financial data including customers, invoices, payments, subscriptions, and transaction history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you start, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://dashboard.stripe.com/register" rel="noopener noreferrer"&gt;Stripe account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Admin or developer access to the Stripe Dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Finding Your API Keys
&lt;/h2&gt;

&lt;p&gt;Stripe provides two types of API keys: publishable keys (for client-side code) and secret keys (for server-side code). Each type has both test and live versions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log into the &lt;a href="https://dashboard.stripe.com/" rel="noopener noreferrer"&gt;Stripe Dashboard&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Developers&lt;/strong&gt; in the left sidebar.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;API keys&lt;/strong&gt; from the menu.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your publishable key (&lt;code&gt;pk_live_&lt;/code&gt; or &lt;code&gt;pk_test_&lt;/code&gt;) and secret key (&lt;code&gt;sk_live_&lt;/code&gt; or &lt;code&gt;sk_test_&lt;/code&gt;) are displayed here. In test mode, you can reveal the secret key as many times as needed. In live mode, you can only reveal a secret key once, so copy and store it immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Restricted API Key
&lt;/h2&gt;

&lt;p&gt;For better security, you can create restricted keys that limit access to specific resources. This is recommended when integrating with third-party services.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the API keys page, click &lt;strong&gt;Create restricted key&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Give the key a descriptive name.&lt;/li&gt;
&lt;li&gt;Set permissions for each resource type. For read-only integrations, select &lt;strong&gt;Read&lt;/strong&gt; for the resources you need access to.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create key&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Common permissions for accounting integrations include read access to: Customers, Charges, PaymentIntents, Invoices, Subscriptions, and Products.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Mode vs Live Mode
&lt;/h2&gt;

&lt;p&gt;Stripe maintains separate environments for testing and production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test mode&lt;/strong&gt; uses keys starting with &lt;code&gt;pk_test_&lt;/code&gt; and &lt;code&gt;sk_test_&lt;/code&gt;. No real money is processed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live mode&lt;/strong&gt; uses keys starting with &lt;code&gt;pk_live_&lt;/code&gt; and &lt;code&gt;sk_live_&lt;/code&gt;. Real transactions occur.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Toggle between modes using the &lt;strong&gt;Test mode&lt;/strong&gt; switch in the Dashboard. Make sure you're using the correct keys for your environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Exposing your secret key&lt;/strong&gt;: Never include your secret key in client-side code, commit it to version control, or share it publicly. Treat it like a password.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using test keys in production&lt;/strong&gt;: If your integration isn't processing real payments, verify you're using live mode keys, not test keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overly permissive keys&lt;/strong&gt;: Use restricted keys with minimal permissions rather than your main secret key. This limits potential damage if a key is compromised.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting to rotate keys&lt;/strong&gt;: If you suspect a key has been exposed, roll it immediately from the API keys page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate Limits
&lt;/h2&gt;

&lt;p&gt;Stripe's API enforces rate limits to ensure platform stability. The default limit for most endpoints is 25 requests per second, though Stripe may increase this for accounts with higher usage. Some endpoints have stricter limits (the Search API and Files API are limited to 20 operations per second). Test mode has lower limits than live mode. If you exceed these limits, Stripe returns a &lt;code&gt;429 Too Many Requests&lt;/code&gt; response.&lt;/p&gt;

&lt;p&gt;For high-volume integrations, implement exponential backoff and cache frequently accessed data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting Stripe via Apideck
&lt;/h2&gt;

&lt;p&gt;Apideck's &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;unified Accounting API&lt;/a&gt; lets you connect to &lt;a href="https://www.apideck.com/integrations/stripe" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt; alongside platforms like &lt;a href="https://www.apideck.com/blog/how-to-get-your-quickbooks-api-key" rel="noopener noreferrer"&gt;QuickBooks&lt;/a&gt; and &lt;a href="https://www.apideck.com/integrations/sage-intacct" rel="noopener noreferrer"&gt;Sage&lt;/a&gt; through a single integration.&lt;/p&gt;

&lt;p&gt;Note that Apideck's Stripe connector uses OAuth rather than standard API keys. You'll need to create a Stripe App first to get your Client ID, Client Secret, and Channel Link ID. See our &lt;a href="https://www.apideck.com/blog/how-to-create-a-stripe-app" rel="noopener noreferrer"&gt;How to Create a Stripe App&lt;/a&gt; guide for the full walkthrough.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring the Connector
&lt;/h3&gt;

&lt;p&gt;With your Stripe App credentials ready, open the Apideck dashboard and navigate to &lt;strong&gt;Configuration &amp;gt; Accounting &amp;gt; Stripe&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/5ML5OXJKhKrQrHeYYUSnGl/8f36e3b1f4838e68b7279753bdca119a/964_1x_shots_so.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/5ML5OXJKhKrQrHeYYUSnGl/8f36e3b1f4838e68b7279753bdca119a/964_1x_shots_so.png" alt="How to Get Your Stripe API Key 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter your Client ID and Client Secret, then scroll down to configure scopes and add your Channel Link ID.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/YtnoKh7urpjwDUAcxIJtn/68f2aaa56ef6fde3aebe3bb803592413/358_1x_shots_so.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/YtnoKh7urpjwDUAcxIJtn/68f2aaa56ef6fde3aebe3bb803592413/358_1x_shots_so.png" alt="How to Get Your Stripe API Key 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What Your Users See
&lt;/h3&gt;

&lt;p&gt;When your users connect their Stripe account through Vault, they'll be prompted for their Stripe Account ID (found under Settings &amp;gt; Account details in Stripe, starts with &lt;code&gt;acct_&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/50HD22csaVTGmjx1PtHFMm/91dbcf21079f16892b859fdfd2f3cc38/853_1x_shots_so.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/50HD22csaVTGmjx1PtHFMm/91dbcf21079f16892b859fdfd2f3cc38/853_1x_shots_so.png" alt="How to Get Your Stripe API Key 3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After authorization, Stripe data flows through Apideck's unified API alongside your other accounting integrations.&lt;/p&gt;

&lt;p&gt;Ready to connect to Stripe and 45+ other accounting platforms through a single API? &lt;a href="https://www.apideck.com/signup" rel="noopener noreferrer"&gt;Get started for free&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>stripe</category>
      <category>unifiedapi</category>
    </item>
    <item>
      <title>An introduction to the Stripe APIs: A Developer's Guide to Every API Surface</title>
      <dc:creator>Kate Apideck</dc:creator>
      <pubDate>Mon, 27 Jul 2026 13:53:38 +0000</pubDate>
      <link>https://dev.to/apideck/an-introduction-to-the-stripe-apis-a-developers-guide-to-every-api-surface-4mfp</link>
      <guid>https://dev.to/apideck/an-introduction-to-the-stripe-apis-a-developers-guide-to-every-api-surface-4mfp</guid>
      <description>&lt;p&gt;Stripe processed $1.9 trillion in total payment volume in 2025. More than 300,000 businesses use Stripe Billing alone, managing close to 200 million active subscriptions. When your platform needs to connect to where money actually flows, Stripe is usually the first integration your customers request.&lt;/p&gt;

&lt;p&gt;But Stripe's API surface is large. Over 500 endpoints across two namespaces (v1 and v2), covering one-time card charges, multi-party marketplace payouts, subscription billing, fraud detection, card issuing, treasury services, and tax compliance. Picking the right API for your use case saves you weeks. This guide covers each major surface, when to reach for it, and how the pieces connect.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Payments API
&lt;/h2&gt;

&lt;p&gt;Most Stripe integrations start here. The central object is the PaymentIntent, which represents a single attempt to collect money from a customer. You create one with an amount and currency, attach a payment method, confirm it, and Stripe handles card network routing, 3D Secure authentication, retry logic, and settlement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.stripe.com/v1/payment_intents &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; sk_test_your_key: &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;usd &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;payment_method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pm_card_visa &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;confirm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Payments API supports over 100 payment methods across 135+ currencies: cards, bank debits (ACH, SEPA), wallets (Apple Pay, Google Pay), and buy-now-pay-later options like Klarna and Affirm. For most SaaS platforms and e-commerce businesses, this is the only API you need to accept money.&lt;/p&gt;

&lt;p&gt;Related endpoints include SetupIntents (saving payment methods without charging) and Refunds. There is also the older Charges API, which still works but lacks support for SCA requirements in Europe and newer payment methods. If you are starting a new integration, use PaymentIntents. Stripe has been steering developers toward this model since 2019, and their &lt;a href="https://www.apideck.com/blog/stripe-llms-txt-instructions-section" rel="noopener noreferrer"&gt;llms.txt instructions section&lt;/a&gt; now explicitly tells AI coding assistants to do the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stripe Billing: Subscriptions and Invoices
&lt;/h2&gt;

&lt;p&gt;Stripe Billing is the revenue engine for SaaS companies. It manages the full lifecycle of recurring payments: creating subscriptions, generating invoices, handling prorations on plan changes, managing trials, and collecting failed payments automatically.&lt;/p&gt;

&lt;p&gt;The core objects are Product, Price, Subscription, and Invoice. A Product describes what you sell. A Price attaches a billing interval and amount to that product. A Subscription ties a customer to one or more prices and tells Stripe when to bill them. Invoices are generated automatically at each billing cycle.&lt;/p&gt;

&lt;p&gt;The complexity Billing handles would take months to build yourself. When a customer on a $100/month plan upgrades to $200/month halfway through a billing cycle, Stripe calculates the prorated amount, generates the right invoice line items, and charges the difference. Subscription schedules let you model phased pricing, free trials that convert to paid plans, and multi-interval billing where different line items renew on different cadences.&lt;/p&gt;

&lt;p&gt;Usage-based billing is another major capability. Stripe Billing supports metered pricing where you report usage throughout a billing period and Stripe calculates the total at invoice time. This model is common among AI and infrastructure companies that charge per API call, per token, or per compute hour. Anthropic, OpenAI, Cursor, and Midjourney all run on Stripe Billing.&lt;/p&gt;

&lt;p&gt;At Stripe Sessions 2025, the company announced Scripts and Workflows: two extensibility primitives that turn Billing into a programmable revenue engine. Scripts let you inject custom business logic into specific API objects. Workflows let you orchestrate multi-step flows across Stripe products using conditional logic. These signal that Stripe views Billing as a platform in its own right.&lt;/p&gt;

&lt;p&gt;For teams that need to sync Stripe Billing data with accounting platforms (QuickBooks, Xero, NetSuite, Sage), Apideck's &lt;a href="https://www.apideck.com/integrations/stripe" rel="noopener noreferrer"&gt;Stripe connector&lt;/a&gt; pulls subscription, invoice, and payment data from Stripe through Apideck's &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;Unified Accounting API&lt;/a&gt; alongside &lt;a href="https://www.apideck.com/blog/top-15-accounting-apis-to-integrate-with" rel="noopener noreferrer"&gt;45+ other accounting connectors&lt;/a&gt;, using a single integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect: Platforms and Marketplaces
&lt;/h2&gt;

&lt;p&gt;Connect is Stripe's API for multi-party money movement. If your platform collects payments from customers and splits the proceeds among sellers, service providers, or other third parties, Connect handles funds flow, onboarding, compliance, and payouts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accounts V2: the recommended path for new platforms
&lt;/h3&gt;

&lt;p&gt;In December 2025, Stripe shipped the &lt;a href="https://docs.stripe.com/connect/accounts-v2" rel="noopener noreferrer"&gt;Accounts V2 API&lt;/a&gt;, which is now the recommended integration path for new Connect platforms. The core improvement: a single &lt;code&gt;Account&lt;/code&gt; object that can be configured as a merchant (to accept payments), a customer (to be charged by your platform), and a recipient (to receive transfers). In the V1 API, representing the same business as both a connected account and a customer required creating and managing separate &lt;code&gt;Account&lt;/code&gt; and &lt;code&gt;Customer&lt;/code&gt; objects with no built-in relationship between them.&lt;/p&gt;

&lt;p&gt;Accounts V2 provides three key advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flexible account configurations.&lt;/strong&gt; You assign &lt;code&gt;merchant&lt;/code&gt;, &lt;code&gt;customer&lt;/code&gt;, or &lt;code&gt;recipient&lt;/code&gt; configurations to an Account to enable different capabilities. Adding a configuration later does not require re-collecting identity requirements that were already provided.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized identity data.&lt;/strong&gt; KYC information is shared across configurations. A connected account that also subscribes to your platform's SaaS product does not need to verify their identity twice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A single API for connected accounts and customers.&lt;/strong&gt; Any API endpoint that accepts a &lt;code&gt;customer&lt;/code&gt; parameter also accepts a &lt;code&gt;customer_account&lt;/code&gt; parameter, so you can use a configured Account in place of a Customer object when creating subscriptions, payment intents, or setup intents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A V2 account creation call looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.stripe.com/v2/core/accounts &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer sk_test_your_key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Stripe-Version: 2025-12-15.clover"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="s1"&gt;'{
    "contact_email": "seller@example.com",
    "display_name": "Example Seller",
    "identity": {
        "country": "us",
        "entity_type": "company"
    },
    "configuration": {
        "merchant": {
            "capabilities": {
                "card_payments": { "requested": true }
            }
        }
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The V1 Accounts and Customers APIs are still fully supported. Stripe does not plan to remove them. You can also use the V2 API to manage accounts that were originally created with V1, including adding the &lt;code&gt;customer&lt;/code&gt; configuration to existing V1 accounts. One caveat: if your platform uses OAuth to authenticate with connected accounts, continue using the V1 APIs for now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Charge models and funds flow
&lt;/h3&gt;

&lt;p&gt;Whether you use V1 or V2, Connect's charge models remain the same. Each seller or provider on your platform gets a Stripe account, and Connect manages the relationship between your platform account and theirs. Three charge models are available: direct charges (the connected account is the merchant of record), destination charges (you collect payment and route funds to a connected account), and separate charges and transfers (you collect payment and transfer funds manually).&lt;/p&gt;

&lt;p&gt;Connect also handles KYC verification, tax form generation (1099s in the US), and payout scheduling. Platforms like Shopify, DoorDash, Lyft, and Airbnb run on Connect. The API supports payouts to bank accounts in 118+ countries, instant payouts for eligible accounts, and cross-border payments in local currencies.&lt;/p&gt;

&lt;p&gt;If you are building a &lt;a href="https://www.apideck.com/industries/vertical-saas" rel="noopener noreferrer"&gt;vertical SaaS&lt;/a&gt; platform or marketplace, Connect is likely the most complex Stripe integration you will encounter. The onboarding flows alone require careful planning around what information to collect, when to verify it, and how to handle incomplete applications or failed identity checks.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/6ebvxbszeu0Vosjz4f7gDR/9dc6487e17001423330a029536d53db8/Screenshot_2026-03-22_at_12.37.35.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/6ebvxbszeu0Vosjz4f7gDR/9dc6487e17001423330a029536d53db8/Screenshot_2026-03-22_at_12.37.35.png" alt="Stripe Connect API"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Radar: Fraud Prevention
&lt;/h2&gt;

&lt;p&gt;Radar is Stripe's machine learning fraud detection system. It runs on every payment processed through Stripe and assigns a risk score based on signals from the broader Stripe network. During Black Friday/Cyber Monday 2024, Radar blocked 20.9 million fraudulent transactions worth $917 million.&lt;/p&gt;

&lt;p&gt;For most businesses, Radar works out of the box. You can also write custom rules using Stripe's rule language to block or review payments based on transaction amount, country of origin, velocity patterns, or metadata values. Radar for Fraud Teams adds manual review queues and more granular controls.&lt;/p&gt;

&lt;p&gt;Risk scores are exposed through the Charge and PaymentIntent objects. You can feed Radar scores into your own fraud models via the API, which is useful if you run a multi-processor setup or have an in-house fraud team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issuing and Treasury
&lt;/h2&gt;

&lt;p&gt;Stripe Issuing lets you create, manage, and distribute virtual and physical payment cards programmatically. The typical use case is corporate expense cards, fleet cards, or rewards cards embedded in your platform. You create a cardholder, issue a card, set spending controls, and receive real-time authorization webhooks when the card is used.&lt;/p&gt;

&lt;p&gt;Treasury provides banking-as-a-service infrastructure: FDIC-insured financial accounts, ACH and wire transfers, and the ability to hold, send, and receive funds. Together, Issuing and Treasury let platforms offer &lt;a href="https://www.apideck.com/industries/fintech" rel="noopener noreferrer"&gt;embedded financial products&lt;/a&gt; (branded debit cards, business checking accounts inside your SaaS product) without becoming a bank.&lt;/p&gt;

&lt;p&gt;Both APIs require additional onboarding with Stripe and typically involve partnerships with underlying bank partners like Evolve Bank or Cross River Bank.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tax, Sigma, and Data Pipeline
&lt;/h2&gt;

&lt;p&gt;Stripe Tax automates sales tax, VAT, and GST calculation and collection across 102 countries. Since Sessions 2025, it also handles registration and filing, making it a full-cycle &lt;a href="https://www.apideck.com/use-cases/tax-automation" rel="noopener noreferrer"&gt;tax compliance&lt;/a&gt; product. The API integrates with Checkout, Billing, and Invoicing, so you can enable tax collection by adding a single parameter to existing API calls.&lt;/p&gt;

&lt;p&gt;Sigma provides a SQL interface for querying your Stripe data directly inside the Dashboard. Data Pipeline exports your Stripe data to external data warehouses (Snowflake, Redshift, BigQuery) on a scheduled basis. Both are useful for finance teams building custom reports, reconciliation workflows, or data models that combine Stripe data with other business data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stripe Is Investing Heavily in Agent Experience
&lt;/h2&gt;

&lt;p&gt;Stripe is one of the few API companies that has recognized a shift in who (or what) consumes its APIs. A growing share of Stripe integration code is now written by AI coding assistants, and Stripe is actively building infrastructure to make those interactions better. The company calls this &lt;a href="https://www.apideck.com/blog/api-design-principles-agentic-era" rel="noopener noreferrer"&gt;agent experience, or AX&lt;/a&gt;, a discipline parallel to developer experience but optimized for autonomous consumers.&lt;/p&gt;

&lt;p&gt;The investments span multiple layers. Stripe hosts an official MCP server at &lt;code&gt;mcp.stripe.com&lt;/code&gt;, giving AI agents direct, OAuth-secured access to Stripe's API. The &lt;code&gt;@stripe/agent-toolkit&lt;/code&gt; provides function-calling integrations for LangChain, OpenAI's Agent SDK, CrewAI, and Vercel's AI SDK in both Python and TypeScript. Stripe also publishes OpenAPI specifications covering both v1 and v2 namespaces in a single file, which helps code generation tools and agent frameworks understand the full API surface.&lt;/p&gt;

&lt;p&gt;The most distinctive piece is Stripe's &lt;a href="https://www.apideck.com/blog/stripe-llms-txt-instructions-section" rel="noopener noreferrer"&gt;llms.txt instructions section&lt;/a&gt;. Buried inside &lt;code&gt;docs.stripe.com/llms.txt&lt;/code&gt; is a set of explicit instructions telling AI coding assistants to always use the Checkout Sessions API over the legacy Charges API, to default to the latest stable SDK version, and to never recommend the legacy Card Element or Sources API. No other API company has shipped anything like this. It is Stripe programming the behavior of every AI coding assistant that fetches that file before helping a developer integrate payments.&lt;/p&gt;

&lt;p&gt;Stripe engineer Ian McCrystal said publicly that he expects AI tools to eventually become the predominant readers of Stripe's documentation. The company is building for that future now. Every docs page is available as clean Markdown (append &lt;code&gt;.md&lt;/code&gt; to any URL), there are three separate llms.txt files across two domains, and the agent toolkit ships with granular permission controls via Restricted API Keys.&lt;/p&gt;

&lt;p&gt;We wrote a &lt;a href="https://www.apideck.com/blog/stripe-llms-txt-instructions-section" rel="noopener noreferrer"&gt;deeper analysis of Stripe's llms.txt approach&lt;/a&gt; and what it means for other API companies. If you run an API, that post is worth reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  API Design and Versioning
&lt;/h2&gt;

&lt;p&gt;Stripe's API follows a REST design with form-encoded request bodies and JSON responses. Authentication uses secret API keys passed via HTTP Basic Auth. Every object has a consistent structure: an &lt;code&gt;id&lt;/code&gt;, an &lt;code&gt;object&lt;/code&gt; type field, and a &lt;code&gt;metadata&lt;/code&gt; hash for attaching your own key-value pairs.&lt;/p&gt;

&lt;p&gt;The API uses a rolling versioning model with named releases. The current major release is Clover (2025-09-30), following Basil and Acacia. Each major release can include breaking changes. Monthly releases within a major version are additive and backwards-compatible. You pin your integration to a specific version using the &lt;code&gt;Stripe-Version&lt;/code&gt; header or your SDK version.&lt;/p&gt;

&lt;p&gt;The v2 namespace (&lt;code&gt;/v2/&lt;/code&gt;) introduces improved idempotency handling, thin events (lightweight webhooks containing only essential data rather than full object snapshots), and a clearer &lt;code&gt;include&lt;/code&gt; parameter for requesting additional fields. You can mix v1 and v2 endpoints in the same integration.&lt;/p&gt;

&lt;p&gt;Webhooks are essential for any production Stripe integration. Stripe sends events to your configured endpoints when objects change state. For billing, you will typically listen for &lt;code&gt;invoice.paid&lt;/code&gt;, &lt;code&gt;invoice.payment_failed&lt;/code&gt;, &lt;code&gt;customer.subscription.updated&lt;/code&gt;, and &lt;code&gt;customer.subscription.deleted&lt;/code&gt;. For payments, &lt;code&gt;payment_intent.succeeded&lt;/code&gt; and &lt;code&gt;payment_intent.payment_failed&lt;/code&gt; are the critical ones. The v2 thin event format reduces payload size and avoids the stale-snapshot problem from earlier webhook implementations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Apideck Fits: Connecting Stripe Billing to the Accounting Stack
&lt;/h2&gt;

&lt;p&gt;Most businesses that use Stripe Billing also use an accounting platform. Invoices generated in Stripe need to appear in the general ledger. Subscription revenue needs to be recognized correctly. Payments need to reconcile against bank transactions.&lt;/p&gt;

&lt;p&gt;Building a direct integration between Stripe and a single accounting platform is straightforward. Building integrations between Stripe and QuickBooks and Xero and NetSuite and Sage Intacct and FreshBooks is a different problem. Each platform has its own API, authentication scheme, data model, and rate limits. Maintaining five or six of these in parallel is a full-time job for an engineering team.&lt;/p&gt;

&lt;p&gt;This is the problem Apideck's &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;Unified Accounting API&lt;/a&gt; solves. One integration, &lt;a href="https://www.apideck.com/integrations" rel="noopener noreferrer"&gt;45+ accounting connectors&lt;/a&gt;, normalized data models, and &lt;a href="https://www.apideck.com/products/vault" rel="noopener noreferrer"&gt;managed authentication&lt;/a&gt;. For teams building &lt;a href="https://www.apideck.com/blog/accounting-integration" rel="noopener noreferrer"&gt;accounting integrations&lt;/a&gt;, it cuts months of development work down to days.&lt;/p&gt;

&lt;p&gt;Apideck's &lt;a href="https://www.apideck.com/integrations/stripe" rel="noopener noreferrer"&gt;Stripe connector&lt;/a&gt; brings subscription, invoice, and payment data from Stripe into the same unified schema that already covers QuickBooks, Xero, NetSuite, Sage, and the rest of the &lt;a href="https://www.apideck.com/blog/the-complete-guide-to-accounting-api-integrations-for-fintech" rel="noopener noreferrer"&gt;accounting ecosystem&lt;/a&gt;. If your platform needs to sync billing data across providers, or your customers use Stripe Billing alongside traditional accounting software, this connector simplifies that connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you are evaluating a Stripe integration, start by identifying which API surface you actually need. For one-time payments, the Payments API is sufficient. For recurring billing, add Stripe Billing. For marketplaces and platforms, add Connect. Layer in Radar, Tax, Issuing, or Treasury based on your specific requirements.&lt;/p&gt;

&lt;p&gt;Stripe's developer documentation at &lt;a href="https://docs.stripe.com" rel="noopener noreferrer"&gt;docs.stripe.com&lt;/a&gt; is among the best in the industry. The API reference includes runnable examples, test mode keys, and client libraries in seven languages. Their Discord server has active Stripe engineers answering technical questions.&lt;/p&gt;

&lt;p&gt;For teams that need Stripe Billing data flowing into their accounting stack alongside other platforms, &lt;a href="https://www.apideck.com" rel="noopener noreferrer"&gt;get started with Apideck&lt;/a&gt; and connect &lt;a href="https://www.apideck.com/integrations/stripe" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt; alongside 45+ other accounting connectors.&lt;/p&gt;

</description>
      <category>api</category>
      <category>stripeapi</category>
      <category>stripe</category>
      <category>unifiedapi</category>
    </item>
    <item>
      <title>Xero Scopes: What Changed and What It Means for Your Integration</title>
      <dc:creator>Kate Apideck</dc:creator>
      <pubDate>Fri, 24 Jul 2026 09:29:40 +0000</pubDate>
      <link>https://dev.to/apideck/xero-scopes-what-changed-and-what-it-means-for-your-integration-36ia</link>
      <guid>https://dev.to/apideck/xero-scopes-what-changed-and-what-it-means-for-your-integration-36ia</guid>
      <description>&lt;p&gt;Xero has replaced two broad OAuth 2.0 scopes with a set of fine-grained scopes that limit each app to only the specific endpoints it needs. If you created your Xero app on or after March 2, 2026, the old broad scopes are simply not available to you. If your app predates that cutoff, you have until September 2027 to migrate.&lt;/p&gt;

&lt;p&gt;This is part of a larger restructuring of the Xero developer platform that also includes &lt;a href="https://dev.to/blog/xero-api-pricing-and-the-app-partner-program"&gt;new API pricing tiers&lt;/a&gt; and premium-gating of the Journals endpoint. The scope changes themselves are a genuine security improvement. But combined with the pricing changes, they create real planning work for any team building on Xero.&lt;/p&gt;

&lt;h2&gt;
  
  
  What exactly changed
&lt;/h2&gt;

&lt;p&gt;Two broad scopes are being deprecated: &lt;code&gt;accounting.transactions&lt;/code&gt; and &lt;code&gt;accounting.reports.read&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before and after: transaction scopes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before (deprecated)&lt;/th&gt;
&lt;th&gt;After (granular)&lt;/th&gt;
&lt;th&gt;Endpoints covered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accounting.transactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;accounting.invoices&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Credit notes, invoices, linked transactions, purchase orders, quotes, repeating invoices, items&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accounting.transactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;accounting.payments&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Batch payments, overpayments, payments, prepayments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accounting.transactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;accounting.banktransactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bank transactions, bank transfers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accounting.transactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;accounting.manualjournals&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Manual journals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accounting.transactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;accounting.classicexpenses&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Expense claims, receipts (deprecated endpoint)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each granular scope also has a &lt;code&gt;.read&lt;/code&gt; variant for read-only access. So if your app only needs to pull invoice data without writing, you request &lt;code&gt;accounting.invoices.read&lt;/code&gt; instead of the full scope.&lt;/p&gt;

&lt;p&gt;One detail worth noting: the items endpoint is available via both the &lt;code&gt;accounting.invoices&lt;/code&gt; and &lt;code&gt;accounting.settings&lt;/code&gt; scopes. If your app only works with items and not invoices, you do not need the invoices scope.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before and after: report scopes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before (deprecated)&lt;/th&gt;
&lt;th&gt;After (granular)&lt;/th&gt;
&lt;th&gt;Reports covered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.aged.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Aged Payables by Contact, Aged Receivables by Contact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.balancesheet.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Balance Sheet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.banksummary.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bank Summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.executivesummary.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Executive Summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.profitandloss.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Profit and Loss&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.trialbalance.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trial Balance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;accounting.reports.taxreports.read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GST and BAS reports&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Scopes that are not changing
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;accounting.contacts&lt;/code&gt;, &lt;code&gt;accounting.settings&lt;/code&gt;, &lt;code&gt;accounting.attachments&lt;/code&gt;, and &lt;code&gt;accounting.budgets&lt;/code&gt; remain available to every app regardless of when it was created. Non-Accounting API scopes like Payroll, Files, Assets, Projects, and the Finance API are also unaffected.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two-track timeline
&lt;/h2&gt;

&lt;p&gt;This is the most important detail for development teams to understand.&lt;/p&gt;

&lt;p&gt;Apps created before March 2, 2026 can continue using the broad &lt;code&gt;accounting.transactions&lt;/code&gt; and &lt;code&gt;accounting.reports.read&lt;/code&gt; scopes until September 2027. During the transition, these apps can use a mix of broad and granular scopes simultaneously. Scopes are additive, so a user who re-authorizes receives both the legacy and new scope grants on their token.&lt;/p&gt;

&lt;p&gt;By end of April 2026, Xero will assign the new granular scopes to all existing apps in the developer portal. But these will not automatically propagate to existing tokens. You need to update your authorization URL to request the granular scopes, and each connected user must re-authorize to receive the new permissions. There is no silent migration path. Explicit user consent is required for every connection.&lt;/p&gt;

&lt;p&gt;Apps created on or after March 2, 2026 have no access to the broad scopes at all. Their authorization links must specify only granular scopes from day one. If a new app calls an endpoint without the matching granular scope, the API returns HTTP 401 Forbidden with an &lt;code&gt;insufficient_scope&lt;/code&gt; error. If you are &lt;a href="https://dev.to/blog/how-to-get-xero-api-key"&gt;setting up a Xero app for the first time&lt;/a&gt;, you will only see the new granular scopes in the developer portal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Journals, manual journals, and bank feeds: three different things
&lt;/h2&gt;

&lt;p&gt;This is where the changes get most confusing, because three closely related concepts are governed by entirely different access regimes.&lt;/p&gt;

&lt;p&gt;Manual journals (the &lt;code&gt;/api.xro/2.0/ManualJournals&lt;/code&gt; endpoint) remain available at all pricing tiers under the new &lt;code&gt;accounting.manualjournals&lt;/code&gt; granular scope. This endpoint handles user-created journal entries and is not restricted beyond requiring the correct scope.&lt;/p&gt;

&lt;p&gt;The Journals endpoint (&lt;code&gt;/api.xro/2.0/Journals&lt;/code&gt;) returns system-generated double-entry journal lines for every transaction in the ledger. This endpoint is now a premium feature gated behind the Advanced pricing tier, which starts at $1,445 AUD per month. Access requires both a security assessment and explicit use-case approval from Xero. If your integration depends on full ledger reconciliation or audit trail functionality, this is the endpoint that matters, and it now comes with a significant cost.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.apideck.com/blog/bank-feeds-api-integration" rel="noopener noreferrer"&gt;Bank feeds&lt;/a&gt; operate under their own separate API and scope (&lt;code&gt;bankfeeds&lt;/code&gt;), which is not affected by the accounting scope changes at all. The Bank Feeds API has its own certification requirement and separate contractual terms. If your app only pushes bank statement data into Xero, the &lt;code&gt;accounting.transactions&lt;/code&gt; deprecation does not impact you.&lt;/p&gt;

&lt;p&gt;Bank transactions, meaning the read and write endpoints for reconciled bank line items within Xero's ledger, move from the old broad scope to &lt;code&gt;accounting.banktransactions&lt;/code&gt;. This is distinct from bank feeds. Bank feeds push raw statement data in. Bank transactions are the resulting accounting records inside Xero.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need to do
&lt;/h2&gt;

&lt;p&gt;If you have an existing Xero integration, the migration path follows a few steps.&lt;/p&gt;

&lt;p&gt;Update your authorization URL to replace the broad scopes with the specific granular scopes your app actually needs. Only request the scopes you use. Requesting everything defeats the purpose and will make users hesitant to authorize.&lt;/p&gt;

&lt;p&gt;Let new users authorize with the new scopes naturally. For existing connected users, let re-authorization happen organically when users add new organizations or refresh their connection. The additive scope behavior means users accumulate both scope sets during the transition.&lt;/p&gt;

&lt;p&gt;Before September 2027, migrate any remaining connections that still only have broad scopes. For holdouts, you can force re-authorization by removing their connection via the API, which requires them to reconnect with the new scopes.&lt;/p&gt;

&lt;p&gt;For testing, create a new app in the Xero developer portal. It will default to granular scopes and can be tested against a Demo Company. No SDK changes are needed since the scopes operate at the OAuth authorization layer.&lt;/p&gt;

&lt;p&gt;If you are building a new integration from scratch, you start with granular scopes by default. Plan your scope requests carefully based on which endpoints you actually need.&lt;/p&gt;

&lt;p&gt;If you are using Apideck to connect to Xero, you can configure which scopes your app requests in the Apideck dashboard. Our &lt;a href="https://developers.apideck.com/connectors/xero/docs/application_owner+oauth_credentials" rel="noopener noreferrer"&gt;Xero connector setup guide&lt;/a&gt; walks through entering your OAuth credentials and reviewing the scopes granted in Xero against the scopes you are requesting in Apideck.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bigger picture
&lt;/h2&gt;

&lt;p&gt;The granular scope change fulfills a long-standing feature request on Xero's UserVoice forum, where developers have been asking for finer-grained permissions since at least 2010. For years, requesting invoice access also exposed bank balances and salary data because there was only one broad scope covering all of it. Fine-grained permissions are a real improvement.&lt;/p&gt;

&lt;p&gt;But the timing is not great. Xero bundled this change with connection-based pricing tiers and premium-gating of key endpoints like Journals. For developers who have been building on the platform for years with free API access, the combined effect feels like a significant shift in the economics of building on Xero.&lt;/p&gt;

&lt;p&gt;This is part of a broader pattern across accounting platforms. As we covered in our &lt;a href="https://www.apideck.com/blog/accounting-and-erp-partnership-guide" rel="noopener noreferrer"&gt;overview of accounting API pricing and partnerships&lt;/a&gt;, every major provider is evolving its developer program in a direction that adds cost and complexity. QuickBooks has its own partnership requirements. Sage remains free but has a smaller market footprint. NetSuite charges separately through its SuiteCloud licensing.&lt;/p&gt;

&lt;p&gt;For teams building &lt;a href="https://dev.to/blog/accounting-integration"&gt;accounting integrations&lt;/a&gt; that need to support multiple platforms, the platform-specific scope and pricing changes reinforce the case for abstracting away provider differences. Whether you build direct integrations or use a unified API layer, the goal is the same: insulate your product from the policy changes of any single platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key resources
&lt;/h2&gt;

&lt;p&gt;Xero's developer blog post on the scope changes: &lt;a href="https://devblog.xero.com/upcoming-changes-to-xero-accounting-api-scopes-705c5a9621a0" rel="noopener noreferrer"&gt;Upcoming changes to Xero Accounting API Scopes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Xero's full scope reference documentation: &lt;a href="https://developer.xero.com/documentation/guides/oauth2/scopes/" rel="noopener noreferrer"&gt;OAuth 2.0 Scopes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a detailed breakdown of the pricing tiers that accompany these scope changes: &lt;a href="https://www.apideck.com/blog/xero-api-pricing-and-the-app-partner-program" rel="noopener noreferrer"&gt;Xero API Pricing and the App Partner Program&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For teams evaluating alternatives alongside Xero: &lt;a href="https://www.apideck.com/blog/top-15-accounting-apis-to-integrate-with" rel="noopener noreferrer"&gt;Top 15 Accounting APIs to Integrate with in 2026&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a broader look at the Xero integration landscape: &lt;a href="https://www.apideck.com/blog/xero-integrations" rel="noopener noreferrer"&gt;Xero Integrations: The Complete Guide for 2026&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>unifiedapi</category>
      <category>xero</category>
    </item>
    <item>
      <title>What Is ERP Banking? The Shift From Portal to Platform</title>
      <dc:creator>Kate Apideck</dc:creator>
      <pubDate>Mon, 20 Jul 2026 20:40:53 +0000</pubDate>
      <link>https://dev.to/apideck/what-is-erp-banking-the-shift-from-portal-to-platform-c85</link>
      <guid>https://dev.to/apideck/what-is-erp-banking-the-shift-from-portal-to-platform-c85</guid>
      <description>&lt;p&gt;Every morning, in corporate finance offices around the world, a treasurer logs into one system to check bank balances, switches to another to initiate payments, then copies data into a spreadsheet to reconcile the general ledger. Nobody designed this workflow. It emerged from decades of banks and accounting platforms evolving independently, and finance teams filling the gaps with manual effort.&lt;/p&gt;

&lt;p&gt;ERP banking is the fix. The idea is simple: instead of making business customers toggle between a bank portal and their accounting software, you embed the banking services directly inside the ERP or accounting system. Payments, balances, reconciliation, cash reporting. All from the same environment where the rest of the financial work happens.&lt;/p&gt;

&lt;p&gt;The term comes from analyst firm Datos Insights. Their definition: ERP banking is a model where corporate clients access and execute banking capabilities directly from their enterprise systems. The bank becomes invisible infrastructure. The customer never leaves their system of record.&lt;/p&gt;

&lt;h2&gt;
  
  
  ERP banking is not ERP for banks
&lt;/h2&gt;

&lt;p&gt;There is a naming collision worth clearing up.&lt;/p&gt;

&lt;p&gt;Search for "ERP banking" and most results describe ERP software used by banks for their own internal operations. SAP for a bank's back office. Oracle for a bank's financial reporting. That is a different topic entirely.&lt;/p&gt;

&lt;p&gt;ERP banking as Datos Insights defines it, and as the commercial banking industry uses the term, describes the opposite direction. Banking services flow into the customer's ERP. The bank is the service provider. The ERP is the destination. One is a procurement decision for the bank's IT department. The other is a product strategy for the bank's commercial banking division.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is happening now
&lt;/h2&gt;

&lt;p&gt;Corporate treasurers are already voting with their feet. Datos Insights surveyed over 1,000 corporate users across 11 countries and found that integration with internal ERP systems is the top priority for corporate treasurers. More than one in four said they would likely switch their primary bank within two years over better technology integration from a competitor. A Nine Wave survey put it more starkly: 85% of CFOs would switch banks for direct ERP-to-bank connectivity. These are not hypothetical preferences. They are already driving churn.&lt;/p&gt;

&lt;p&gt;The same Datos Insights research found that 58% of businesses already use a fintech for a core cash management or treasury service. Every payment that runs through a fintech instead of a bank is revenue the bank loses. ERP banking is how banks respond: by meeting the customer inside the system where financial decisions actually get made, rather than waiting for them to log into a portal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.openbankingtracker.com" rel="noopener noreferrer"&gt;Open banking&lt;/a&gt; regulations in Europe (PSD2), the UK, and increasingly the US (Section 1033) forced banks to build API capabilities for consumer data access. Those same APIs now serve ERP integration use cases. The plumbing exists. The question is who builds the last mile into the accounting system.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works technically
&lt;/h2&gt;

&lt;p&gt;ERP banking connects a bank's core systems to the customer's ERP or accounting platform. The goal is bidirectional data flow: the bank pushes transaction data and balances into the ERP, and the ERP sends payment instructions back to the bank.&lt;/p&gt;

&lt;p&gt;Datos Insights identifies five connectivity methods, roughly in order of maturity:&lt;/p&gt;

&lt;p&gt;File-based transfers using SFTP, BAI2, MT940, and ISO 20022 are the oldest method and still the most common at many institutions. The bank generates a file, uploads it to a server, and the ERP picks it up on a schedule. Reliable but slow. No real-time error handling. Frequent format mismatches between bank and ERP.&lt;/p&gt;

&lt;p&gt;Host-to-host connections are direct, dedicated links between a bank and a corporate customer's systems. Common in large enterprise relationships. Expensive to set up and maintain, which makes them impractical for mid-market customers.&lt;/p&gt;

&lt;p&gt;Bank APIs are the foundation of modern ERP banking. The bank publishes RESTful endpoints that the ERP or a middleware provider can call to retrieve balances, initiate payments, and pull transaction history. J.P. Morgan, HSBC, Deutsche Bank, PNC, and TD Bank all offer this kind of API access for corporate clients.&lt;/p&gt;

&lt;p&gt;ERP gateways and feeds are native connectivity features built into the ERP platforms themselves. Oracle Fusion Cloud ERP supports direct &lt;a href="https://www.apideck.com/blog/bank-api-integration" rel="noopener noreferrer"&gt;bank API integration&lt;/a&gt; from J.P. Morgan as a built-in capability. SAP has similar gateway functionality.&lt;/p&gt;

&lt;p&gt;ERP banking connectors are pre-built plugins from middleware providers like FISPAN. They connect a bank's services directly into specific ERP platforms. FISPAN's connector works with Oracle NetSuite, Sage Intacct, Microsoft Dynamics 365, QuickBooks Online, and Xero. The bank partners with FISPAN. The customer installs the connector. No custom development required.&lt;/p&gt;

&lt;p&gt;These methods are additive. A bank typically supports several simultaneously because its customer base spans different platforms and company sizes. A Fortune 500 on SAP might use host-to-host. A mid-market company on &lt;a href="https://www.apideck.com/integrations/oracle-netsuite" rel="noopener noreferrer"&gt;NetSuite&lt;/a&gt; uses an ERP banking connector. A small business on &lt;a href="https://www.apideck.com/integrations/quickbooks" rel="noopener noreferrer"&gt;QuickBooks&lt;/a&gt; uses a bank feed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does for the customer
&lt;/h2&gt;

&lt;p&gt;The use cases span the full commercial banking value chain. For a detailed breakdown with examples from J.P. Morgan, HSBC, Bank of America, Deutsche Bank, and others, see our companion post on &lt;a href="https://dev.to/blog/accounting-erp-integration-banks-use-cases"&gt;accounting and ERP integration for banks&lt;/a&gt;. Here is the summary.&lt;/p&gt;

&lt;p&gt;Payment initiation is the most visible use case. The customer creates a payment run inside their ERP and the bank processes it directly. No portal login required. TD Bank supports this for NetSuite, QuickBooks Online, Sage Intacct, and Dynamics 365 Business Central through its embedded banking product.&lt;/p&gt;

&lt;p&gt;Bank feeds and transaction sync follow closely. The bank pushes transaction data into the customer's &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;accounting system&lt;/a&gt; automatically. Challenger banks like Allica Bank and Monzo have turned this into a competitive differentiator, offering free integration with &lt;a href="https://www.apideck.com/integrations/xero" rel="noopener noreferrer"&gt;Xero&lt;/a&gt;, QuickBooks, and Sage as part of their business banking products.&lt;/p&gt;

&lt;p&gt;Cash position reporting pulls balance and transaction data from all banking relationships into a single &lt;a href="https://dev.to/blog/treasury-management-systems-multi-bank-connectivity-integrations"&gt;treasury management system&lt;/a&gt; or ERP dashboard. No more logging into five different bank portals every morning.&lt;/p&gt;

&lt;p&gt;Automated reconciliation matches bank transactions against ERP records automatically. Exceptions get flagged for review. The line-by-line manual matching goes away.&lt;/p&gt;

&lt;p&gt;Expense sync lands corporate card transactions in the customer's accounting platform without anyone touching a spreadsheet. J.P. Morgan's Touchless Expense program and Cross River (powering Divvy/Bill.com) both work this way.&lt;/p&gt;

&lt;p&gt;Credit underwriting from accounting data connects banks and lenders to a business's accounting system through an API to pull real-time P&amp;amp;L, cash flow, and accounts receivable data. Plaid and Datos Insights found that 60% of US small business lenders now use some form of account data in their underwriting process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is building it
&lt;/h2&gt;

&lt;p&gt;The market has three layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Banks with direct integrations
&lt;/h3&gt;

&lt;p&gt;J.P. Morgan is furthest along. It became the first bank with direct integrated banking for Oracle Fusion Cloud ERP, and also partnered with FISPAN for its Treasury Ignition product covering NetSuite, Sage Intacct, and Dynamics 365. Over 1,000 corporates are live on Treasury Ignition.&lt;/p&gt;

&lt;p&gt;TD Bank launched its Embedded Banking product with FISPAN across four ERP platforms. HSBC and Deutsche Bank both partnered with FinLync for SAP connectivity. PNC, Citizens, KeyBank, Wells Fargo, BMO, and Santander all have FISPAN partnerships. BMO launched BMO Sync in early 2025 as the first fully embedded banking solution from a Canadian bank.&lt;/p&gt;

&lt;h3&gt;
  
  
  Middleware and connector providers
&lt;/h3&gt;

&lt;p&gt;FISPAN is the clear market leader. Founded in 2016, it now has over 4,500 businesses on its platform processing more than $100 billion in annual payment volume. The company raised a $30 million Series B in mid-2025 from Canapi Ventures. Sage named FISPAN its recommended bank feeds solution for Sage Intacct, and over 1,000 businesses are now active on that integration alone, with adoption growing 118% year-over-year.&lt;/p&gt;

&lt;p&gt;FinLync focuses on the enterprise SAP segment. Cobase provides multi-bank connectivity for European corporates. Fides connects to over 13,000 banks globally for &lt;a href="https://dev.to/blog/treasury-management-systems-multi-bank-connectivity-integrations"&gt;treasury connectivity&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unified API providers
&lt;/h3&gt;

&lt;p&gt;For banks that need to connect to many accounting and ERP platforms at once, a &lt;a href="https://www.apideck.com/unified-apis/accounting" rel="noopener noreferrer"&gt;unified API&lt;/a&gt; provides a single normalized interface that maps to multiple downstream systems. Instead of building separate integrations for QuickBooks, Xero, Sage, NetSuite, &lt;a href="https://www.apideck.com/integrations/exact-online" rel="noopener noreferrer"&gt;Exact Online&lt;/a&gt;, &lt;a href="https://www.apideck.com/integrations/myob" rel="noopener noreferrer"&gt;MYOB&lt;/a&gt;, and &lt;a href="https://www.apideck.com/integrations/dynamics-365-business-central" rel="noopener noreferrer"&gt;Microsoft Dynamics 365&lt;/a&gt;, the bank builds one integration and gets connectivity to all supported platforms through a single schema.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;Apideck's Accounting API&lt;/a&gt; supports 45+ accounting and ERP connectors. The bank's engineering team writes to one API. Apideck handles the platform-specific translation, authentication, and lifecycle management. This approach is particularly valuable for the accounting data side of ERP banking: bank feeds, transaction sync, invoice reconciliation, and credit underwriting all require deep integration with the customer's accounting platform, and each platform has a different API with different data models.&lt;/p&gt;

&lt;p&gt;The distinction between middleware connectors and unified APIs matters here. FISPAN and FinLync solve the bank-to-ERP connectivity problem: getting payment capabilities and balance data into the ERP. Unified APIs like Apideck solve the ERP-to-accounting-data problem: reading and writing financial data across dozens of accounting platforms through a single integration. For banks building a full ERP banking stack, both layers come into play.&lt;/p&gt;

&lt;h2&gt;
  
  
  The market
&lt;/h2&gt;

&lt;p&gt;Datos Insights estimates the ERP banking market at $11 billion to $19 billion, growing at nearly 10% annually.&lt;/p&gt;

&lt;p&gt;The retention argument alone justifies the investment. Once banking services are embedded in a customer's ERP workflow, switching banks becomes significantly more disruptive. FISPAN reports that banks on its platform see 30-50% deposit increases from connected clients. And 8% of FISPAN users are net-new to their commercial bank, meaning the embedded banking capability is winning accounts, not just retaining them.&lt;/p&gt;

&lt;p&gt;Datos Insights published a separate report specifically on monetizing ERP banking, exploring pricing models from premium connectivity fees to transaction-based billing to value-added bundling within commercial banking packages.&lt;/p&gt;

&lt;p&gt;J.P. Morgan's 2026 payment trends report found that 88% of financial professionals consider direct bank connectivity very or extremely beneficial. 85% said they would likely switch banks to get it. At that volume of demand, banks don't get to treat this as optional.&lt;/p&gt;

&lt;h2&gt;
  
  
  The accounting layer problem
&lt;/h2&gt;

&lt;p&gt;Most of the attention in ERP banking goes to the bank connectivity side. How does the bank's API connect to the ERP platform? That matters, but it is only half the problem.&lt;/p&gt;

&lt;p&gt;The other half is the &lt;a href="https://www.apideck.com/blog/accounting-integration" rel="noopener noreferrer"&gt;accounting layer&lt;/a&gt;. A bank can push transactions into an ERP all day long, but if those transactions do not reconcile against the customer's chart of accounts, match invoices, or flow into journal entries correctly, the integration creates more problems than it solves.&lt;/p&gt;

&lt;p&gt;This is where fragmentation hits hardest. Each accounting platform has its own API with its own data model for transactions, its own authentication scheme, its own field naming conventions. A bank that wants to support QuickBooks Online, Xero, Sage Intacct, NetSuite, &lt;a href="https://www.apideck.com/integrations/freshbooks" rel="noopener noreferrer"&gt;FreshBooks&lt;/a&gt;, and Dynamics 365 needs six different accounting integrations. Building and maintaining those one at a time is expensive. Every engineer working on &lt;a href="https://dev.to/blog/erp-api-integration"&gt;accounting platform connectors&lt;/a&gt; is an engineer not working on core banking products. This is the problem &lt;a href="https://dev.to/blog/what-is-a-unified-api"&gt;unified accounting APIs&lt;/a&gt; exist to solve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this goes
&lt;/h2&gt;

&lt;p&gt;ERP banking is a platform shift in commercial banking, not a feature.&lt;/p&gt;

&lt;p&gt;The banks that do it well will become invisible to their customers in the best possible way. The middleware providers and &lt;a href="https://www.apideck.com/industries/banking" rel="noopener noreferrer"&gt;unified API platforms&lt;/a&gt; connecting banks, ERPs, and accounting systems are the infrastructure layer making that transition possible.&lt;/p&gt;

&lt;p&gt;For any bank evaluating its ERP banking strategy, the demand case is settled. The remaining problem is how to reach broad accounting platform coverage without burning through engineering resources on one-at-a-time integrations. The largest banks can build natively to Oracle and SAP. Everyone else needs the combination of ERP banking connectors for the bank-to-ERP pipe and &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;unified accounting APIs&lt;/a&gt; for the accounting data pipe.&lt;/p&gt;

&lt;p&gt;Apideck's &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;Accounting API&lt;/a&gt; connects to 45+ accounting and ERP platforms through a single integration. If your bank needs to build accounting integrations at scale, &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;get started here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>erpbanking</category>
      <category>unifiedapi</category>
    </item>
    <item>
      <title>Bank API Integration: How Banks Connect to Accounting and ERP Systems</title>
      <dc:creator>Kate Apideck</dc:creator>
      <pubDate>Wed, 15 Jul 2026 16:11:22 +0000</pubDate>
      <link>https://dev.to/apideck/bank-api-integration-how-banks-connect-to-accounting-and-erp-systems-4ga8</link>
      <guid>https://dev.to/apideck/bank-api-integration-how-banks-connect-to-accounting-and-erp-systems-4ga8</guid>
      <description>&lt;p&gt;Banks no longer compete on branch locations or interest rates alone. The fastest-growing differentiator in commercial banking is how well a bank plugs into the accounting and ERP systems its customers already use every day. This shift is often called &lt;a href="https://dev.to/blog/accounting-erp-integration-banks-use-cases"&gt;ERP banking&lt;/a&gt;, and it is reshaping how commercial banks compete for business customers.&lt;/p&gt;

&lt;p&gt;Bank API integration is the technical bridge that makes this possible. It connects a bank's core systems to third-party accounting platforms, ERPs, and financial applications through standardized interfaces. When done well, it eliminates manual file transfers, enables real-time cash visibility, and turns the bank into an invisible layer inside the customer's existing workflow.&lt;/p&gt;

&lt;p&gt;This guide covers how bank API integration works, the use cases driving adoption, the engineering challenges involved, and the approaches available for building and scaling these integrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is bank API integration?
&lt;/h2&gt;

&lt;p&gt;Bank API integration refers to any API-based connection between a bank and a third-party application. The third party is typically an ERP system like Oracle NetSuite or SAP, an accounting platform like QuickBooks Online or Xero, or a fintech application that handles payments, expense management, or treasury operations.&lt;/p&gt;

&lt;p&gt;Once the connection is established, financial data flows between the bank and the third-party system on a defined cadence. That data can include transaction records, account balances, payment instructions, invoice details, and reconciliation status.&lt;/p&gt;

&lt;p&gt;There are two distinct perspectives on bank API integration, and they matter because the technical requirements differ:&lt;/p&gt;

&lt;p&gt;The bank's perspective. A bank builds integrations with accounting and ERP platforms to offer embedded financial services to its business customers. The goal is to reduce churn, win new commercial accounts, and differentiate against competitors who still rely on portal-based workflows. J.P. Morgan, HSBC, TD Bank, and PNC all run embedded banking programs that connect directly into customer ERP environments.&lt;/p&gt;

&lt;p&gt;The business's perspective. A company connects its bank accounts to its own accounting or ERP system to automate cash management, reconciliation, and payment workflows. The goal is operational efficiency. Finance teams want real-time visibility into bank balances without logging into separate portals, and they want payment runs initiated from within their system of record.&lt;/p&gt;

&lt;p&gt;Both perspectives converge on the same technical problem: connecting bank systems to accounting platforms at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why APIs beat the alternatives
&lt;/h2&gt;

&lt;p&gt;Banks and businesses have historically relied on four methods to move financial data between systems: manual entry, file-based transfers, screen scraping, and APIs. Here is why APIs are winning.&lt;/p&gt;

&lt;p&gt;File-based transfers (SFTP/BAI2/MT940) still dominate in legacy commercial banking. The bank generates a file in BAI2 or MT940 format, uploads it to an SFTP server, and the customer's ERP system picks it up on a schedule. This works, but it introduces latency (often 24 hours), requires manual intervention when file formats change, and creates reconciliation gaps when transactions arrive out of order. File-based integrations also lack real-time error handling. If a payment file contains a malformed record, the entire batch may fail silently.&lt;/p&gt;

&lt;p&gt;Screen scraping extracts data from a bank's online portal by simulating user interactions. It breaks every time the bank updates its UI. It also creates security risks because it requires storing and transmitting user credentials. Most banks actively block scraping, and regulatory frameworks like PSD2 in Europe have replaced it with dedicated API access.&lt;/p&gt;

&lt;p&gt;APIs solve these problems. They provide structured, authenticated, real-time access to financial data. Authentication is handled through OAuth 2.0 or API keys with scoped permissions. Data is encrypted in transit. Rate limits prevent abuse. And when something fails, the API returns a structured error response that the consuming application can handle programmatically.&lt;/p&gt;

&lt;p&gt;The fourth option, manual entry, still exists in small businesses but is not a serious integration method. It does not scale, it introduces human error, and it consumes finance team hours that could be spent on higher-value work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of bank APIs
&lt;/h2&gt;

&lt;p&gt;Not all bank APIs are the same. The access model and scope vary significantly depending on the bank's strategy and regulatory environment.&lt;/p&gt;

&lt;p&gt;Private APIs are internal to the bank. They connect the bank's own systems, such as the core banking platform, the online portal, and the mobile app. Third parties cannot access these. They matter for bank API integration only insofar as they constrain what data the bank can expose through its other APIs.&lt;/p&gt;

&lt;p&gt;Partner APIs are shared with selected third parties under a contractual agreement. A bank might expose payment initiation and account information endpoints to a specific ERP vendor or fintech partner. FISPAN, for example, partners with banks like J.P. Morgan, PNC, and TD Bank to embed banking services inside ERP platforms through partner API access. These integrations are typically bespoke and require a formal relationship.&lt;/p&gt;

&lt;p&gt;Open APIs are available to any authorized developer. In the EU, PSD2 mandates that banks provide &lt;a href="https://www.openbankingtracker.com/open-banking-api" rel="noopener noreferrer"&gt;open banking APIs&lt;/a&gt; for account information (AIS) and payment initiation (PIS) to licensed third-party providers. The UK's Open Banking Implementation Entity defined standardized API specifications that the nine largest UK banks must support. In the US, the CFPB's Section 1033 rulemaking is pushing toward formal consumer data access standards, though the market currently relies more on data aggregators like Plaid and MX than on bank-published open APIs. The &lt;a href="https://www.openbankingtracker.com" rel="noopener noreferrer"&gt;Open Banking Tracker&lt;/a&gt; tracks 54,000+ financial institutions across 30+ jurisdictions, providing a comprehensive view of which banks offer open API access and under what regulatory framework.&lt;/p&gt;

&lt;p&gt;Bank-as-a-Service (BaaS) APIs are a newer category. Companies like Solaris, Column, and Treasury Prime provide full-stack banking APIs that non-bank companies can use to offer financial products. Samsung Pay, for example, runs on Solaris APIs. This is adjacent to traditional bank API integration but follows a different pattern: instead of connecting to an existing bank, the company embeds a bank's infrastructure through APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use cases driving bank API integration
&lt;/h2&gt;

&lt;p&gt;The market for bank-ERP integration is estimated at $11 billion to $19 billion and growing at nearly 10% annually, according to Datos Insights research. Here are the use cases driving that growth. (For a deeper dive with specific bank examples from J.P. Morgan, HSBC, Deutsche Bank, and others, see our companion post on &lt;a href="https://dev.to/blog/accounting-erp-integration-banks-use-cases"&gt;accounting and ERP integration for banks&lt;/a&gt;.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Bank feeds and transaction sync
&lt;/h3&gt;

&lt;p&gt;This is the most common use case and the foundation for everything else. The bank sends transaction data (debits, credits, balances) to the customer's accounting system automatically. In accounting terms, this is the bank feed.&lt;/p&gt;

&lt;p&gt;Challenger banks like Allica Bank and Monzo have turned accounting integration into a competitive weapon for business banking. Allica provides free integration with &lt;a href="https://www.apideck.com/integrations/quickbooks" rel="noopener noreferrer"&gt;QuickBooks&lt;/a&gt;, &lt;a href="https://www.apideck.com/integrations/xero" rel="noopener noreferrer"&gt;Xero&lt;/a&gt;, and Sage as part of its Business Rewards Account. Monzo gates accounting integration behind its paid business tiers. Both approaches reflect the same reality: bank feeds are no longer an optional add-on.&lt;/p&gt;

&lt;p&gt;For the bank building these integrations, the challenge is supporting the dozen-plus accounting platforms its customers actually use. QuickBooks Online, Xero, Sage Intacct, FreshBooks, Zoho Books, Wave, and &lt;a href="https://www.apideck.com/integrations/dynamics-365-business-central" rel="noopener noreferrer"&gt;Microsoft Dynamics 365&lt;/a&gt; each have different API schemas, authentication flows, and data models for representing transactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Payment initiation from ERP
&lt;/h3&gt;

&lt;p&gt;Instead of logging into a bank portal to initiate payments, the customer creates a payment run inside their ERP and the bank processes it directly. TD Bank's Embedded Banking product supports this for NetSuite, QuickBooks Online, Sage Intacct, and Microsoft Dynamics 365 Business Central. Customers can initiate ACH, wire, and check payments without leaving their ERP.&lt;/p&gt;

&lt;p&gt;For the bank, this is a retention play. Once payment initiation is embedded in a customer's ERP workflow, switching banks becomes significantly more disruptive. For the customer, it eliminates the dual-entry problem where payment details have to be entered in both the ERP and the bank portal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Invoice reconciliation
&lt;/h3&gt;

&lt;p&gt;A vendor creates an invoice through their bank, sends it to the customer, and the invoice data syncs to the vendor's &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;accounting system&lt;/a&gt;. When the customer pays, the payment data flows back into the accounting system and matches against the outstanding invoice. The vendor's accounting team reconciles without manual intervention.&lt;/p&gt;

&lt;p&gt;This workflow requires bidirectional sync between the bank and the accounting platform. The bank needs to push invoice metadata (amount, date, currency, due date) to the accounting system on creation, and then update the invoice status when payment is received.&lt;/p&gt;

&lt;h3&gt;
  
  
  Corporate card provisioning and expense sync
&lt;/h3&gt;

&lt;p&gt;Banks that offer corporate card programs can integrate with their customers' HR and accounting systems to automate card provisioning. When a new employee is added to the HRIS, the bank provisions a corporate card with spending limits based on the employee's role, department, and location. When the employee is terminated, the card is deactivated automatically.&lt;/p&gt;

&lt;p&gt;On the expense side, corporate card transactions sync directly into the customer's &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;accounting platform&lt;/a&gt;. J.P. Morgan's Touchless Expense program and Cross River (powering Divvy/Bill.com) both operate this way. The bank pushes categorized transaction data into the accounting system, eliminating manual expense reports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Credit underwriting from accounting data
&lt;/h3&gt;

&lt;p&gt;This may be the most transformative use case. By accessing a business's real-time accounting data (profit and loss statements, cash flow, accounts receivable aging, and outstanding obligations), lenders can make faster and more accurate credit decisions than traditional methods allow.&lt;/p&gt;

&lt;p&gt;Research by Plaid and Datos Insights found that 60% of US small business lenders now use some form of account data in their underwriting process. This is one of the clearest examples of &lt;a href="https://www.openbankingtracker.com/open-finance-map" rel="noopener noreferrer"&gt;open finance&lt;/a&gt; in action: instead of relying on annual financial statements and manual document collection, the lender connects to the business's accounting system through an API and pulls real-time financial data.&lt;/p&gt;

&lt;p&gt;For banks building this capability, the integration needs to support read access to the full chart of accounts, journal entries, invoices, bills, and bank transactions across whatever accounting platform the applicant uses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cash position reporting
&lt;/h3&gt;

&lt;p&gt;Treasury teams at mid-market and enterprise companies need real-time visibility into cash positions across multiple bank accounts, entities, and currencies. Without an integration, this means logging into multiple bank portals, downloading statements, and consolidating data in a spreadsheet.&lt;/p&gt;

&lt;p&gt;With bank API integration, balance and transaction data from all banking relationships flows into a single treasury management system or ERP. Kyriba, for example, connects to over 9,900 banks to provide this kind of unified cash visibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  The engineering challenge
&lt;/h2&gt;

&lt;p&gt;The use cases are clear. The engineering challenge is what makes bank API integration hard in practice.&lt;/p&gt;

&lt;p&gt;Each accounting platform and ERP has its own API with its own data model, authentication scheme, rate limits, and field conventions. A bank that wants to support QuickBooks Online, Xero, Sage Intacct, &lt;a href="https://www.apideck.com/integrations/oracle-netsuite" rel="noopener noreferrer"&gt;NetSuite&lt;/a&gt;, Microsoft Dynamics 365, and FreshBooks needs to build and maintain six separate integrations.&lt;/p&gt;

&lt;p&gt;Each of those integrations involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implementing the platform's authentication flow (OAuth 2.0 for most, but the details vary)&lt;/li&gt;
&lt;li&gt;Mapping the bank's internal data model to the platform's schema for transactions, invoices, contacts, accounts, and journal entries&lt;/li&gt;
&lt;li&gt;Handling pagination, rate limits, webhook subscriptions, and retry logic&lt;/li&gt;
&lt;li&gt;Managing ongoing API version changes, field deprecations, and breaking changes&lt;/li&gt;
&lt;li&gt;Testing against sandbox environments, which not all platforms provide equally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Datos Insights research across 1,000+ corporate users in 11 countries found that more than one in four corporate treasurers will likely switch their primary financial institution within two years because a competitor offers better technology integration. The stakes are high, but the engineering cost of building and maintaining integrations one at a time is equally high.&lt;/p&gt;

&lt;p&gt;Banks that try to build everything in-house face a painful tradeoff: every engineer working on accounting platform integrations is an engineer not working on core banking products. And the integration surface area keeps growing as new accounting platforms gain market share and existing platforms evolve their APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How unified APIs solve the fragmentation problem
&lt;/h2&gt;

&lt;p&gt;A &lt;a href="https://www.apideck.com/unified-apis/accounting" rel="noopener noreferrer"&gt;unified API&lt;/a&gt; provides a single normalized interface that maps to multiple downstream platforms. Instead of building separate integrations for QuickBooks, Xero, Sage, NetSuite, and the rest, the bank builds one integration against the unified API and gets connectivity to all supported platforms through a single schema.&lt;/p&gt;

&lt;p&gt;This approach works because accounting platforms share a common conceptual model despite differing implementations. Every accounting system has some version of accounts, transactions, invoices, bills, contacts, journal entries, and tax rates. The unified API abstracts over the implementation differences while preserving access to the underlying data.&lt;/p&gt;

&lt;p&gt;For banks, the advantages are significant:&lt;/p&gt;

&lt;p&gt;Speed. One integration instead of six or twelve. A bank can go from zero accounting platform connectivity to broad coverage in weeks instead of months.&lt;/p&gt;

&lt;p&gt;Maintenance. The unified API provider handles downstream API changes, field mapping updates, and authentication flow changes. The bank's engineering team stays focused on its core product.&lt;/p&gt;

&lt;p&gt;Coverage. A unified accounting API like &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;Apideck's&lt;/a&gt; supports 40+ accounting and ERP connectors through a single integration. As new platforms gain market share, the bank gets access without additional engineering work.&lt;/p&gt;

&lt;p&gt;Normalized data. The bank works with a single schema for invoices, transactions, and journal entries regardless of which accounting platform the customer uses. This simplifies downstream processing, reporting, and analytics.&lt;/p&gt;

&lt;p&gt;The tradeoff is control. A unified API introduces a dependency and an abstraction layer. Banks that need deep, platform-specific functionality (custom fields, platform-specific workflows, advanced reporting APIs) may find that the unified model does not cover every edge case. The right approach depends on the bank's integration needs, engineering capacity, and the breadth of platforms it needs to support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approaches to building bank API integration
&lt;/h2&gt;

&lt;p&gt;There are three primary approaches. Each involves a different set of tradeoffs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build native integrations in-house
&lt;/h3&gt;

&lt;p&gt;The bank's engineering team builds and maintains each integration directly against the target platform's API.&lt;/p&gt;

&lt;p&gt;This gives the bank full control over data mapping, sync cadence, error handling, and feature depth. It also means the bank can build highly customized integrations that match its customers' exact requirements.&lt;/p&gt;

&lt;p&gt;The downside is cost and speed. Building a production-quality integration with a single accounting platform takes weeks to months, and maintaining it is an ongoing engineering commitment. Banks that choose this path typically support only two or three platforms and leave the rest of the market unserved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Expose API endpoints for customers to build against
&lt;/h3&gt;

&lt;p&gt;Larger banks often publish their own APIs and let customers or their ERP vendors build the integration. This shifts the engineering burden to the customer side.&lt;/p&gt;

&lt;p&gt;The bank avoids building and maintaining integrations itself, but the customer experience suffers. Customers may take months to build to the bank's endpoints, and ongoing maintenance falls on their engineering teams. For small and mid-market businesses without dedicated integration engineers, this approach is not viable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use a unified API
&lt;/h3&gt;

&lt;p&gt;The bank integrates once with a unified API provider and gets normalized access to multiple accounting and ERP platforms. &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;Apideck's Accounting API&lt;/a&gt;, for example, provides a single integration point that connects to &lt;a href="https://www.apideck.com/integrations/quickbooks" rel="noopener noreferrer"&gt;QuickBooks Online&lt;/a&gt;, &lt;a href="https://www.apideck.com/integrations/xero" rel="noopener noreferrer"&gt;Xero&lt;/a&gt;, &lt;a href="https://www.apideck.com/integrations/sage-intacct" rel="noopener noreferrer"&gt;Sage Intacct&lt;/a&gt;, &lt;a href="https://www.apideck.com/integrations/netsuite" rel="noopener noreferrer"&gt;NetSuite&lt;/a&gt;, FreshBooks, Exact Online, &lt;a href="https://www.apideck.com/integrations/myob" rel="noopener noreferrer"&gt;MYOB&lt;/a&gt;, and &lt;a href="https://www.apideck.com/integrations" rel="noopener noreferrer"&gt;many others&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the fastest path to broad coverage and the lowest ongoing maintenance burden. The bank's engineering team writes to one API schema, and the unified API provider handles the platform-specific translation, authentication, and API lifecycle management.&lt;/p&gt;

&lt;p&gt;Banks that need to move quickly, support a wide range of customer accounting platforms, and keep their engineering team focused on core banking products should evaluate the unified API approach seriously.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to evaluate a bank API integration strategy
&lt;/h2&gt;

&lt;p&gt;The right approach depends on several factors specific to the bank's situation.&lt;/p&gt;

&lt;p&gt;How many accounting platforms do your customers use? If the answer is two (say, QuickBooks and Xero), native integrations may be viable. If the answer is eight or more, building natively becomes prohibitively expensive.&lt;/p&gt;

&lt;p&gt;How fast do you need to go live? If there is competitive pressure to launch ERP-embedded banking quickly, a unified API cuts time-to-market from months to weeks.&lt;/p&gt;

&lt;p&gt;What is the depth of integration required? Simple bank feeds require a narrow API surface. Full bidirectional sync with payment initiation, invoice management, and journal entry creation requires deeper integration. Make sure whatever approach you choose supports the data objects and operations your use cases demand.&lt;/p&gt;

&lt;p&gt;What is the total cost of ownership? Factor in initial build time, ongoing maintenance, developer hiring, sandbox access fees, and the opportunity cost of engineers not working on core banking products.&lt;/p&gt;

&lt;p&gt;Does the provider offer sandbox environments? Testing accounting integrations against live customer data is risky. Sandbox environments with realistic test data are essential for development and QA.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open banking is not the same thing
&lt;/h2&gt;

&lt;p&gt;Open banking and bank API integration overlap but are not the same thing.&lt;/p&gt;

&lt;p&gt;Open banking is a regulatory and industry framework that requires banks to provide API access to authorized third parties. It is driven by regulation (&lt;a href="https://www.openbankingtracker.com/regulations" rel="noopener noreferrer"&gt;PSD2 in Europe&lt;/a&gt;, Open Banking in the UK, Section 1033 in the US) and focuses on consumer data portability and payment initiation. The scope is expanding toward &lt;a href="https://www.openbankingtracker.com/open-finance" rel="noopener noreferrer"&gt;open finance&lt;/a&gt;, which extends the same principles beyond payments and accounts to insurance, investments, pensions, and lending data.&lt;/p&gt;

&lt;p&gt;Bank API integration is a broader category that includes any API-based connection between a bank and a third-party system. It covers open banking use cases but also extends to ERP integrations, accounting platform connections, treasury management, and embedded banking services that are not mandated by regulation.&lt;/p&gt;

&lt;p&gt;A bank can have a robust open banking API program and still lack proper integrations with the accounting platforms its commercial customers use. The two capabilities serve different customer segments and different business objectives.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;The direction is clear. According to Datos Insights, more than one in four corporate treasurers will likely switch banks within two years if a competitor offers better technology integration. Banks that embrace &lt;a href="https://dev.to/blog/accounting-erp-integration-banks-use-cases"&gt;ERP banking&lt;/a&gt; and embed their services inside the accounting workflows their customers already rely on will retain and grow their commercial banking relationships. Banks that force customers to use separate portals and manual file transfers will lose them.&lt;/p&gt;

&lt;p&gt;The technical infrastructure to make this work exists today. The question for any bank is whether to build it all in-house, piece by piece, or use a &lt;a href="https://www.apideck.com/unified-apis/accounting" rel="noopener noreferrer"&gt;unified accounting API&lt;/a&gt; to get there faster.&lt;/p&gt;

&lt;p&gt;Apideck's &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;Accounting API&lt;/a&gt; connects to 30+ accounting and ERP platforms through a single integration. If your bank needs to build accounting integrations at scale, &lt;a href="https://www.apideck.com/accounting-api" rel="noopener noreferrer"&gt;get started here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>bankapi</category>
      <category>unifiedapi</category>
    </item>
  </channel>
</rss>
