MoneyForward Cloud Integration

MoneyForward Cloud Integration #

MoneyForward Cloud is a suite of Japanese cloud accounting products. BonsAI integrates with Cloud Accounting (クラウド会計) and Cloud Invoice (クラウド請求書) for master data synchronization and AR invoice reads.

Architecture Overview #

MoneyForward is not a single-app integration like Xero or QBO. Each MF Cloud product is a separate service, but Cloud Accounting and Cloud Invoice share a unified OAuth auth server at https://api.biz.moneyforward.com. This lets a single OAuth app request scopes from both products in one authorization flow.

User → BonsAI OAuth authorize → MF App Portal consent
                                    ↓
                               auth code callback
                                    ↓
                        BonsAI exchanges for tokens
                                    ↓
              ┌─────────────────────────────────────────────┐
              │   Cloud Accounting API (master data)        │
              │   https://api-accounting.moneyforward.com   │
              ├─────────────────────────────────────────────┤
              │   Cloud Invoice API (AR invoices)           │
              │   https://invoice.moneyforward.com          │
              └─────────────────────────────────────────────┘

Products & Scopes #

Cloud Accounting (クラウド会計) #

Provides COA master data: accounts, taxes, departments, trade partners, journal entries.

Scope Access
mfc/accounting/offices.read Office/company info
mfc/accounting/accounts.read Chart of accounts
mfc/accounting/departments.read Departments
mfc/accounting/taxes.read Tax rates/codes
mfc/accounting/trade_partners.read Trade partners (contacts)
mfc/accounting/trade_partners.write Create/update trade partners
mfc/accounting/journal.read Journal entries
mfc/accounting/journal.write Create/update/delete journal entries (publish)
mfc/accounting/voucher.write Upload/unlink 証憑 voucher files (publish attachments)

Scope migration: journal.write and voucher.write were added for publish. The App Portal app registration must include them, and existing connections must re-authorize before publish works (API calls under the old consent return 403 for the new scopes).

Cloud Invoice (クラウド請求書) #

Provides AR invoice data: billings, partners, items, quotes.

Scope Access
mfc/invoice/data.read All Invoice API objects (read-only)

Multiple scopes are space-delimited in the authorize URL:

scope=mfc/accounting/offices.read mfc/accounting/accounts.read mfc/invoice/data.read

Auth Endpoints #

All endpoints below are for the unified auth server shared by Cloud Accounting and Cloud Invoice.

Endpoint URL
Authorization https://api.biz.moneyforward.com/authorize
Token https://api.biz.moneyforward.com/token
Revoke https://api.biz.moneyforward.com/revoke

Client authentication method: CLIENT_SECRET_BASIC (HTTP Basic Auth with client_id:client_secret in the Authorization header).

Token Lifetimes #

Token Lifetime Notes
Access token 1 hour (3600s)
Refresh token 18 months (~540 days / 46,656,000s) Expiry resets on each use
Auth code 10 minutes

Refresh token rotation: Each refresh issues a new access + refresh token pair. The old tokens are invalidated immediately. The token store must persist both the new access_token and the new refresh_token from every refresh response.

API Base URLs #

Product API Base
Cloud Accounting https://api-accounting.moneyforward.com/api/v3/
Cloud Invoice https://invoice.moneyforward.com/api/v3/

OAuth App Registration #

App Portal URL #

https://biz.moneyforward.com/app_portal

Navigate from the MF Cloud management console → sidebar → App Portal.

Pre-requisites #

Before registering an OAuth app, the MoneyForward Cloud account must have:

  1. An active MF Cloud subscription with Cloud Accounting and/or Cloud Invoice enabled (any plan tier).
  2. A user with 全権管理者 (superadmin) permission to activate the App Portal for the first time.
  3. A user with アプリ開発 (App Development) permission to register the app and obtain credentials.
  4. For production: a separate user with アプリ連携 (App Integration) permission to authorize the OAuth consent screen (MF best practice: keep dev and auth roles separate in production).

Registration Steps #

  1. Log into MoneyForward Cloud management console.

  2. Open App Portal from the left sidebar.

  3. Click 「アプリを作成」 (Create App).

  4. Fill in the registration form:

    Field Value
    App name BonsAI (Cloud Accounting + Invoice) [env]
    Redirect URI https://[bonsapi-host]/api/v1/integrations/oauth/authorize/callback
    Client auth method CLIENT_SECRET_BASIC
    Scopes Select all required scopes listed above
  5. After creation, copy the Client ID and Client Secret.

  6. Store credentials in Doppler (see Credential Storage).

Per-Environment Configuration #

Register one app per environment:

Environment App Name Suffix Redirect URI Host
dev_local [dev] http://localhost:8080
dev_aws [dev] Dev environment bonsapi host
prod [prod] Production bonsapi host

Full callback URL pattern:

{bonsapi-host}/api/v1/integrations/oauth/authorize/callback

Credential Storage #

Doppler Keys #

All MoneyForward credentials are stored in Doppler under project bonsai:

Doppler Key Description
MONEY_FORWARD_CLIENT_ID OAuth Client ID
MONEY_FORWARD_CLIENT_SECRET OAuth Client Secret

These are set per Doppler config (dev_local, dev_aws, prod).

Local Development #

For local development, credentials can be configured in .config.yaml:

moneyforward:
  client_id: ""
  client_secret: ""

These are mapped to environment variables by override-coder-env.sh.

Credential Rotation #

  1. Create new credentials in the MF App Portal:

    • Open App Portal → select the app → regenerate client secret.
    • The old secret remains valid until you explicitly revoke it (if MF supports dual secrets), or is immediately invalidated (check portal behavior).
  2. Update Doppler:

    doppler secrets set MONEY_FORWARD_CLIENT_SECRET="new-secret" \
      --project bonsai --config <env>
    
  3. Restart services to pick up new secrets:

    kubectl rollout restart deployment/bonsapi-deployment
    
  4. Verify the integration still works by triggering a token refresh.

  5. Document the rotation in the change log.

Revoking a Connection #

To revoke a user’s OAuth connection:

  1. Via API — POST to the revoke endpoint:

    POST https://api.biz.moneyforward.com/revoke
    Content-Type: application/x-www-form-urlencoded
    Authorization: Basic base64(client_id:client_secret)
    
    token={refresh_token}
    
  2. Via MF Portal — The connected user can disconnect the app from their MF Cloud settings → 連携アプリ管理 (Connected App Management).

  3. Verify revocation — After revoking, confirm a 401 Unauthorized response when using the old access token:

    curl -H "Authorization: Bearer {old_access_token}" \
      https://api-accounting.moneyforward.com/api/v3/offices
    # Expected: 401 Unauthorized
    

Publish (AP bills / AR invoices / Direct expenses) #

MoneyForward Cloud Accounting has no document-level object — publish creates a journal (仕訳) via POST /api/v3/journals, with one main-side row per line and the contra side consolidated per account (the layout BonsAI’s Journal Entry Review shows — e.g. three expense debits against a single 小口現金 credit carrying the summed value). Branches may carry only one side (CRUDJournalLine requires neither debitor nor creditor); the journal balances as a whole and reads back through our own journal-classification rules:

Document Debit (借方) Credit (貸方)
AP bill expense line account, per line 買掛金 (or per-line credit override in double-entry mode), consolidated per account
AR invoice 売掛金 (or per-line debit override), consolidated per account revenue line account, per line
Direct expense expense line account, per line header paid-from account (or per-line credit override), consolidated per account

Consolidated contra rows keep the tax rate / department only when uniform across the merged lines (mixed values are dropped rather than guessed), matching the Journal Entry Review.

Key behaviors:

  • Republish is delete + re-create (DELETE /api/v3/journals/{id} then POST /api/v3/journals), the same should_delete_before_publish flow as Sage/freee; update_invoice / update_direct_expense are intentionally Unimplemented. The journal id and 仕訳番号 change on republish, so external_id is re-persisted and 証憑 are re-uploaded from the extraction pages. Unpublish is DELETE.
  • Amounts are tax-inclusive integer yen. The write schema has no tax_value — MoneyForward derives tax from value + tax_id, so it is the source of truth for tax after publish.
  • Attachments are 証憑 vouchers (POST /api/v3/vouchers, Base64): max 5 MB per file, max 5 per journal. DELETE /api/v3/vouchers only unlinks (電帳法 — the file survives in the クラウドBox trash). Download is not possible (Cloud Box has no GA download API).
  • Detail fetch is lenient. fetch_invoice / fetch_direct_expense first try the strict AR/AP/expense shape; when a journal we published does not match (user-chosen debit/credit account in double-entry mode, non-REVENUE/EXPENSE line account) they log a warning and fall back to a shape-agnostic read (all non-contra lines, journal total) instead of NotFound. Only the list endpoints keep the strict filter. This matters because the post-publish attachment sync re-reads the journal through fetch_invoice; a NotFound there failed the publish and retried forever.
  • Single-entry mode resolves the contra account to the office’s sole 買掛金/売掛金 account and errors if none or several exist; double-entry mode uses the per-line debit/credit selections.
  • 取引先 are addressed by trade partner code (our contact ids already are codes).
  • 部門 (departments) map from the line’s first tag; more than one tag per line is rejected.
  • インボイス区分 is sent as INVOICE_KIND_QUALIFIED only when the document carries a valid 適格請求書発行事業者番号 (T-number); otherwise it is omitted so MF applies its default.
  • Journals have no payment status, so published documents are always Authorized; the paid-invoice delete guard in accounting-sync never triggers for MoneyForward.
  • Auto-publish defaults are switched off on first connect (same policy as Sage/freee).
  • Bank statement publish is not yet implemented (candidate follow-up: journals per transaction, or the 明細 POST /api/v3/transactions + journalize bank-feed route, scope transaction.write).

Verify against a real office (no sandbox exists): tax derivation vs user-edited tax amounts under 税込/税抜 term settings, sub_account_id+parent pairing, and voucher re-upload across republish.

Important Constraints #

No Sandbox Environment #

MoneyForward Cloud does not provide a sandbox or test environment. All dev/staging testing requires a real paid MF Cloud account. This is a known limitation and must be budgeted for during development.

  • Each environment (dev, staging, prod) needs its own OAuth app registration.
  • Test data created during development exists in the real MF Cloud account.
  • Use a dedicated development MF Cloud account to avoid polluting customer data.

Region #

MoneyForward Cloud operates Japan only. There is a single global API endpoint with no regional split.

Rate Limits #

Refer to the MoneyForward API documentation for current rate limit policies. The Cloud Accounting API applies per-endpoint rate limits.

Future Products (Post-MVP) #

The following MF Cloud products use separate legacy OAuth servers and will require independent OAuth app registrations if integrated:

Product Auth Server Status
Cloud Expense (クラウド経費) https://expense.moneyforward.com/oauth/ Post-MVP
Cloud Accounts Payable (クラウド債務支払) https://payable.moneyforward.com/oauth/ Post-MVP

Troubleshooting #

“Invalid Redirect URI” during OAuth #

  • Verify the redirect URI in the MF App Portal matches exactly (including protocol and trailing slashes).
  • Check that the bonsapi host in the environment matches the registered redirect URI.

Token Refresh Fails #

  • Refresh tokens are single-use. If a refresh was attempted but the response was not persisted (e.g., crash mid-refresh), the old refresh token is already invalidated.
  • The user must re-authorize through the OAuth flow.
  • Check that both access_token and refresh_token are saved after every refresh.

403 on API Calls #

  • Verify the requested scope was included in the OAuth authorization.
  • Check that the MF Cloud account has the required product subscription active.

See Also #