<?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>Embedded Lending: How Revenue Financing Platforms Use Banking, Accounting, and Commerce Data</title>
      <dc:creator>Kate Apideck</dc:creator>
      <pubDate>Thu, 20 Aug 2026 13:14:58 +0000</pubDate>
      <link>https://dev.to/apideck/embedded-lending-how-revenue-financing-platforms-use-banking-accounting-and-commerce-data-5bca</link>
      <guid>https://dev.to/apideck/embedded-lending-how-revenue-financing-platforms-use-banking-accounting-and-commerce-data-5bca</guid>
      <description>&lt;p&gt;&lt;a href="https://www.shopify.com/capital" rel="noopener noreferrer"&gt;Shopify Capital&lt;/a&gt; originated $4.2 billion in merchant cash advances and loans in 2025, up from $3 billion in 2024. &lt;a href="https://www.wayflyer.com/" rel="noopener noreferrer"&gt;Wayflyer&lt;/a&gt; has deployed over $5 billion to more than 5,000 small businesses. &lt;a href="https://www.parafin.com/" rel="noopener noreferrer"&gt;Parafin&lt;/a&gt; has extended over $25 billion in offers across Amazon, DoorDash, Walmart, and dozens of vertical SaaS platforms. These numbers keep climbing because these companies figured out something traditional banks never did: the best way to underwrite an SMB is to watch it operate in real time, across every system where money moves.&lt;/p&gt;

&lt;p&gt;The common thread is data. Not a single data source, but three layered together: banking transactions, accounting records, and commerce platform activity. Each source tells a different part of the story. Together they tell the whole thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What banking, accounting, and commerce data reveal to lenders
&lt;/h2&gt;

&lt;p&gt;Banking data shows cash reality. It tracks what comes in and what goes out, including overdraft patterns and loan repayments to other lenders. When Wayflyer onboards an ecommerce merchant, the first thing they request is access to bank account data via open banking aggregators. Their underwriting team uses that transaction history to assess business performance and creditworthiness. But as Wayflyer discovered when scaling across markets, raw open banking data is messy. Transaction descriptions are inconsistent. Categorization is unreliable. They partnered with Ntropy specifically to enrich and standardize bank transaction data so their ML models could actually interpret what merchants were spending money on. Spending at Amazon versus spending at a restaurant means two very different things to an underwriter.&lt;/p&gt;

&lt;p&gt;Accounting data adds structure and context that bank statements alone can't provide. A profit and loss statement shows margins. A balance sheet shows liabilities. Accounts receivable data reveals whether customers pay on time or 90 days late. Accounts payable data shows whether a business is under pressure from creditors. This is why the lending industry has moved toward standardizing accounting data alongside banking and commerce sources through &lt;a href="https://www.apideck.com/use-cases/business-lending-loan-underwriting" rel="noopener noreferrer"&gt;unified APIs&lt;/a&gt;. Lenders who rely on bank statements alone miss critical signals. A business might show healthy bank balances while carrying mounting trade payables that signal distress. Or a business might look cash-poor while sitting on $500K in outstanding invoices from creditworthy customers.&lt;/p&gt;

&lt;p&gt;Commerce data completes the picture. Platforms like Shopify and Amazon Seller Central know daily sales volumes, refund rates, customer acquisition trends, and seasonal patterns. Shopify Capital uses machine learning to analyze exactly these signals, including sales history, disputes, and customer engagement, to generate pre-approved offers for merchants without them even applying. The AI makes the first pass. A human reviews before funds move. Repayment happens automatically as a percentage of daily sales.&lt;/p&gt;

&lt;h2&gt;
  
  
  How revenue financing platforms onboard merchants
&lt;/h2&gt;

&lt;p&gt;Across nearly every revenue based financing platform that has scaled, the onboarding flow follows the same structure. The merchant connects their commerce platform, then their bank accounts via open banking or Plaid, then their accounting software. The more data sources a merchant connects, the faster the decision and the better the terms.&lt;/p&gt;

&lt;p&gt;Paperstack, which picked up former Clearco customers, underwrites ecommerce loans based on ecommerce, accounting, and financial data. Their team reviews connected data and presents funding offers within 24 to 48 hours, with fees ranging from 4% to 12% depending on repayment history and risk profile. Uncapped follows a similar model in Europe, connecting to commerce, payment, accounting, and advertising platforms to build a composite view of business health.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://swoopfunding.com/" rel="noopener noreferrer"&gt;Swoop&lt;/a&gt; takes a different approach as a funding marketplace rather than a direct lender. Operating across the UK, Ireland, US, Canada, Australia, and South Africa with over 200,000 clients and 1,000+ funding providers, Swoop matches businesses to the right financing option using data from their bank accounts and accounting systems. They use open banking for transaction data and connect to accounting platforms like Sage through &lt;a href="https://www.apideck.com/accounting-api/payments" rel="noopener noreferrer"&gt;Apideck's unified Accounting API&lt;/a&gt;. That Sage integration became a global co-marketing partnership, giving Swoop distribution into Sage's customer base across multiple markets. Swoop's COO Ciaran Burke has said that access to richer data from bank accounts and accounting platforms allows for better lending decisions and faster capital.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/d6o5ai4eeewt/6HHiDrTliEDK4GWFg5A72a/2099ad77c00ea78f019e5ed0ca018b36/swoop-integrations.webp" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/d6o5ai4eeewt/6HHiDrTliEDK4GWFg5A72a/2099ad77c00ea78f019e5ed0ca018b36/swoop-integrations.webp" alt="Swoop Funding Integrations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.fundwell.com/" rel="noopener noreferrer"&gt;Fundwell&lt;/a&gt;, which connects businesses with over 60 funding partners across more than 50 industries, has automated its document analysis and risk profiling to the point where underwriting workload dropped by 30 hours per week, supporting a 76.6% increase in onboarded customers. The pattern is the same everywhere: connect the data, automate the assessment, compress the time to funding.&lt;/p&gt;

&lt;p&gt;The speed advantage is real. According to a comparison published by Luca AI, approval speed correlates directly with data integration depth. Platforms with direct API access to commerce, payment, and accounting systems approve in hours. Those requiring manual financial statement uploads take weeks. Traditional banks take 6 to 12 weeks minimum. Fifty-seven percent of all SME credit applications in the UK are either abandoned because the process is too hard or ultimately rejected, according to Plaid's analysis of the UK market.&lt;/p&gt;

&lt;h2&gt;
  
  
  Embedded lending changes the integration model
&lt;/h2&gt;

&lt;p&gt;The most interesting shift in SMB lending is happening inside platforms themselves. Instead of merchants going to a separate lender, the lending is embedded into the software they already use.&lt;/p&gt;

&lt;p&gt;Parafin powers embedded capital programs for major marketplaces and vertical SaaS companies alike. Their underwriting engine runs on ML models trained on data from over 2 million small businesses. When a DoorDash restaurant gets a pre-approved capital offer inside the DoorDash app, Parafin is running the underwriting behind the scenes using the merchant's sales history on the platform. The merchant never fills out a loan application or submits a credit check. The financing appears inside the platform they already operate in. Parafin ranked 15th on the 2025 Inc. 5000 list, which gives you a sense of how fast this model is growing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kanmon.com" rel="noopener noreferrer"&gt;Kanmon&lt;/a&gt; targets a similar opportunity but focuses specifically on vertical SaaS and marketplace platforms. Kanmon provides the embedded lending infrastructure (term loans, lines of credit, invoice financing, AP financing) so that platforms can offer capital to their SMB customers without becoming lenders themselves. Kanmon handles underwriting, compliance, and servicing behind the scenes. What's notable is that Kanmon explicitly states platforms don't need to offer embedded payments before offering embedded lending, though underwriting benefits from payment data when it's available. The company analyzes financial information and business history to tailor risk assessments per product type, with most customers funded within one to two days. You can find Kanmon alongside other embedded lending providers in the &lt;a href="https://www.openbankingtracker.com/embedded-finance" rel="noopener noreferrer"&gt;Open Banking Tracker's embedded finance directory&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stripe.com/capital" rel="noopener noreferrer"&gt;Stripe Capital&lt;/a&gt; works the same way for businesses processing payments through Stripe. Eligibility requires at least 90 days on the platform and $1,000 in average monthly sales. Transaction history and dispute rate determine the offer. Squarespace launched embedded lending powered by Stripe Capital in January 2025 and surpassed its entire annual goals within a single quarter, according to Corey Zettler, Director of Product for Financial Solutions at Squarespace.&lt;/p&gt;

&lt;p&gt;Shopify takes it furthest. Capital flex, launched in November 2025, gives qualifying merchants continuous access to funds with a credit limit that adjusts based on real-time business metrics. There is no application and no fixed loan term. It is a rolling line of credit powered by the data Shopify already collects from running the merchant's entire commerce operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for vertical SaaS and embedded finance builders
&lt;/h2&gt;

&lt;p&gt;The pattern here is clear, and it has direct implications for anyone building lending products, vertical SaaS platforms, or embedded finance features.&lt;/p&gt;

&lt;p&gt;Single-source underwriting is dying. Banking data alone misses accounting context. Accounting data alone can be stale or manipulated. Commerce data alone lacks the full financial picture. The winning models combine all three through a single integration layer. When open banking data is paired with real-time accounting data, lenders can cross-reference actual financials with what was submitted in the application, catching fraud and improving accuracy simultaneously. This is why unified APIs that connect accounting, banking, and commerce data in a single integration have become critical infrastructure for lending platforms.&lt;/p&gt;

&lt;p&gt;That makes the integration layer the real moat. The lenders winning in this market are not the ones with the most capital. They are the ones with the deepest integrations into the systems where business data lives. Shopify can underwrite better than any bank because it sees every transaction, every refund, every customer dispute. Parafin can extend offers across dozens of platforms because it built configurable underwriting infrastructure that can run inside a partner's environment without the partner even sharing raw merchant data with Parafin's servers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.floatfinance.com/" rel="noopener noreferrer"&gt;Float Financial&lt;/a&gt; in Canada shows where this is heading for vertical SaaS. Float started as a spend management platform with corporate cards and expense software. It now serves over 6,000 Canadian businesses, raised a $100 million debt facility from Silicon Valley Bank in January 2026, and launched Float Charge, a short-term working capital product. Float's visibility into real-time spending data and accounting integrations (NetSuite, Xero) positions it to underwrite working capital loans using data it already collects from running its customers' daily financial operations. Sacra's analysis notes that this positions Float for the $40 billion Canadian SMB lending market. That trajectory, from software platform to embedded lender, is one that any vertical SaaS company with transaction data could follow.&lt;/p&gt;

&lt;p&gt;The opportunity for vertical SaaS companies follows directly from this. If your platform processes payments, manages inventory, or handles accounting for your customers, you are sitting on the data that lenders need. The embedded lending model, powered by infrastructure providers like Parafin or Kanmon, or built with &lt;a href="https://www.apideck.com/use-cases/business-lending-loan-underwriting" rel="noopener noreferrer"&gt;unified APIs for accounting data&lt;/a&gt;, turns that data into a revenue stream and a retention tool simultaneously.&lt;/p&gt;

&lt;p&gt;The $901 million revenue based financing market from 2019 was projected to reach $42.3 billion by 2027. Looking at the origination volumes from Shopify, Wayflyer, and Parafin alone, that projection looks conservative. The platforms that control the data layer, connecting banking, accounting, and commerce in a single onboarding flow, are the ones writing the checks.&lt;/p&gt;

</description>
      <category>embeddedlending</category>
    </item>
    <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>
  </channel>
</rss>
