Short description
- A minimal Spring Boot app that demonstrates the QuickBooks Online Accounting Batch API: posting batch payloads, optional idempotent retries via
requestid, parsing per-item results, and surfacing raw responses. Includes basic payload validation aligned with documentation (≤ 50 items,bIdrequired).
If the embedded player does not render in your environment, use the direct link:
https://github.intuit.com/noneal/SampleApp-Webhooks-Java-CloudEvents/assets/86120/5171a8da-6b18-4fab-b5b6-69743cdec7bb
Local demo video (in this repo)
Direct link to the file in this repo:
src/main/resources/static/images/Batch_Example.mp4?raw=1
- Java 17 or higher
- Gradle 7.0 or higher
- QuickBooks Developer account and a QuickBooks Online company
- ngrok (for local development)
- Clone and run
git clone [email protected]:noneal/SampleApp-Batch-Java.git
cd SampleApp-Batch-Java
./gradlew bootRun- Spring Boot Web and Thymeleaf
- QuickBooks Java SDKs:
ipp-v3-java-devkit,oauth2-platform-api,ipp-v3-java-data(6.7.0) - JAXB for Java 17 compatibility:
javax.xml.bind:jaxb-api,org.glassfish.jaxb:jaxb-runtime - Jackson Databind
- Spring Session Core
- Spring Boot Starter Test (test scope)
- Configure env (example)
export QB_CLIENT_ID=your_client_id
export QB_CLIENT_SECRET=your_client_secret
export QB_REDIRECT_URI=https://your-ngrok-url.ngrok-free.app/callback
export QB_ENVIRONMENT=production # or sandbox
export QB_GRAPHQL_URL=https://qb.api.intuit.com/graphql
export QB_BASE_URL=https://quickbooks.api.intuit.com
export QB_MINOR_VERSION=75
export QB_SCOPES="com.intuit.quickbooks.accounting"- Start ngrok to 8080 and set redirect URI in your app to
https://<ngrok-domain>/callback
- Visit
http://localhost:8080 - Click "Connect to QuickBooks" to authenticate
- Use “Step 2: Accounting Batch API”
- Paste a batch payload (JSON) into the textarea.
- Optionally provide a
requestId(≤ 36 chars) to demo idempotent retries. - Submit to call
POST /v3/company/{realmId}/batch?minorversion={mv}[&requestid=...]. - The app shows:
- Totals (success/fault)
- Per-item status (by
bId) - Raw response (pretty-printed)
- Basic validation before send:
BatchItemRequestis an array- Count ≤ 50 (per docs)
- Each item has a
bId
- Example payload:
{
"BatchItemRequest": [
{ "bId": "C01", "operation": "create", "Customer": { "DisplayName": "Sandbox Demo Customer 001" } },
{ "bId": "Q01", "Query": "SELECT Id, DisplayName FROM Customer STARTPOSITION 1 MAXRESULTS 5" }
]
}- Idempotent retry (requestId): send the same URL/body twice:
POST /v3/company/{realmId}/batch?minorversion=75&requestid=7f7a1c1c-5c4f-4b2a-8e0c-abcd1234abcd
- Business rules highlighted in the app:
- ≤ 50 items per batch
- ≤ 40 batch requests per minute per realmId
- Items are independent; execution order isn’t guaranteed
- Use
requestid+ stablebIds for safe retries
/— Home page/qbo-login— Initiates OAuth flow/callback— OAuth callback handler/batch— Executes a batch payload (optionalrequestIdform field)/logout— Clears session and logs out
- Import
postman/QBO-Batch.postman_collection.jsoninto Postman. - Set collection variables:
- baseUrl (default
https://quickbooks.api.intuit.com) - realmId, accessToken, minorversion (75), requestId
- baseUrl (default
- Run:
- “Query - First 5 Customers”
- “Batch - Idempotent Create + Query” (send twice with the same
requestIdto demo idempotency)
- Forbidden (403) or Insufficient Scope
- Ensure both scopes are configured and granted during auth
- Re-authenticate after changing scopes
- Invalid client or redirect URI
- Verify Client ID/Secret
- Ensure
redirect-uriexactly matches the app settings in the Developer Portal
- Environment mismatch
- Make sure
environmentmatches the company you are connecting to (sandbox vs production)
- JAVA_HOME is set to an invalid directory (e.g.,
/usr/bin/java)
- Symptom: Gradle prints
ERROR: JAVA_HOME is set to an invalid directory: /usr/bin/java. - Cause:
JAVA_HOMEpoints to the java launcher, not the JDK folder that containsbin/javac. - Fix (macOS, zsh):
/usr/libexec/java_home -V # list installed JDKs export JAVA_HOME=$(/usr/libexec/java_home -v 17) export PATH="$JAVA_HOME/bin:$PATH" # persist for new shells echo 'export JAVA_HOME=$(/usr/libexec/java_home -v 17)' >> ~/.zshrc echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> ~/.zshrc source ~/.zshrc
- Using jenv: If you use
jenv, prefer configuring via jenv instead of manually settingJAVA_HOME:jenv versions jenv global 17 jenv which java echo $JAVA_HOME
- Linux example:
# find your JDK install path, then set e.g. export JAVA_HOME=/usr/lib/jvm/java-17-openjdk export PATH="$JAVA_HOME/bin:$PATH"
- Windows (PowerShell):
setx JAVA_HOME "C:\\Program Files\\Eclipse Adoptium\\jdk-17" setx PATH "$($env:JAVA_HOME)\\bin;$env:PATH"
- Gradle-only fallback: Create
~/.gradle/gradle.propertieswith:org.gradle.java.home=/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home
-
ERR_NGROK_8012 (connection refused to upstream)
- Symptom: ngrok shows target
http://localhost:3000but your app listens on 8080. - Fix: Start ngrok to 8080:
ngrok http 8080or changeserver.portto 3000.
- Symptom: ngrok shows target
-
Wrong redirect URI during OAuth
- Ensure
quickbooks.redirect-urimatches your current ngrok domain and ends with/callback. - Update
QB_REDIRECT_URIenv var orapplication.yml, and in the Intuit Developer Portal.
- Ensure
-
Tunnel not active / 404
- Start ngrok before starting auth, and use the HTTPS forwarding URL in your app config.
- Confirm the URL in the browser matches the latest ngrok session domain.
-
Mixed environment URLs
- Sandbox company requires sandbox base URL:
https://sandbox-quickbooks.api.intuit.com. - Production uses
https://quickbooks.api.intuit.com.
- Sandbox company requires sandbox base URL:
-
Local firewall/port in use
- Verify the app is running: open
http://localhost:8080directly. - If port is busy, change
server.portinapplication.ymland re-run:ngrok http <new-port>.
- Verify the app is running: open
SampleApp-Batch-Java/
├── src/main/java/com/quickbooks/demo/
│ ├── Application.java
│ ├── config/
│ ├── controller/
│ └── service/
├── src/main/resources/
│ ├── application.yml
│ ├── graphql/
│ ├── static/
│ └── templates/
└── README.md
This project is licensed under the Apache License 2.0 — see LICENSE.
For support, visit the Intuit Developer Community (https://help.developer.intuit.com/s/) or open an issue in this repository.
- Topics: quickbooks, batch-api, accounting-api, java, spring-boot, qbo, idempotency, rest, oauth2