sdk_python icon indicating copy to clipboard operation
sdk_python copied to clipboard

Listing company exports via SDK returns no items, but direct REST call works when user id is explicit

Open hunter-nl opened this issue 2 months ago • 1 comments

I’m integrating the official Python SDK to list and export customer statements (MT940/PDF) across both personal and company monetary accounts, and I’ve run into issues that seem to be SDK-specific:

Listing company exports via SDK returns no items, but direct REST call works when user id is explicit Context With a personal API key that has access to company accounts, I can:

  • list accounts (personal + company),
  • create and download statement exports for those accounts,
  • but cannot list existing exports for company-owned accounts via the SDK’s list; it returns an empty set.

Workaround that succeeds

  • If I call the REST endpoint using the owning user’s id explicitly, I get the expected results.
  • Path: user/{OWNER_USER_ID}/monetary-account/{ACCOUNT_ID}/customer-statement
  • Query params (e.g., count=50) work as expected.

Evidence from verbose logs (ids and names anonymized)

  • [debug] SDK list signature mismatch: ExportStatementApiObject.list() got an unexpected keyword argument 'count'. is related to #180

[debug] Listing exports for account id={PERSON_ACCOUNT_ID}, owner='{PERSON_NAME}', ownerUserId={PERSON_USER_ID}, ownerType=Unknown, params={'count': 50, 'newer_id': None, 'older_id': None} [debug] SDK list signature mismatch: ExportStatementApiObject.list() got an unexpected keyword argument 'count'. Retrying with required-only param -> parsed 52 items.

[debug] Listing exports for account id={COMPANY_ACCOUNT_ID_A}, owner='{COMPANY_NAME_A}', ownerUserId={COMPANY_USER_ID_A}, ownerType=Unknown, params={'count': 50, 'newer_id': None, 'older_id': None} [debug] SDK list signature mismatch: ExportStatementApiObject.list() got an unexpected keyword argument 'count'. Retrying with required-only param -> empty set. [debug] Fallback GET user/{COMPANY_USER_ID_A}/monetary-account/{COMPANY_ACCOUNT_ID_A}/customer-statement params={'count': '50'} -> bytes=17225 [debug] Fallback parsed 12 items for acc_id={COMPANY_ACCOUNT_ID_A}

[debug] Listing exports for account id={COMPANY_ACCOUNT_ID_B}, owner='{COMPANY_NAME_B}', ownerUserId={COMPANY_USER_ID_B}, ownerType=Unknown, params={'count': 50, 'newer_id': None, 'older_id': None} [debug] SDK list signature mismatch: ExportStatementApiObject.list() got an unexpected keyword argument 'count'. Retrying with required-only param. [debug] Fallback GET user/{COMPANY_USER_ID_B}/monetary-account/{COMPANY_ACCOUNT_ID_B}/customer-statement params={'count': '50'} -> bytes=26727 [debug] Fallback parsed 19 items for acc_id={COMPANY_ACCOUNT_ID_B}

Interpretation

  • The SDK list implementation seems to bind to the current session’s user id (personal), then attempts GET user/{SESSION_USER_ID}/monetary-account/{ACCOUNT_ID}/customer-statement. If the monetary account is owned by a company (a different user id), that call returns empty results rather than the company’s actual statements.
  • When I direct the call to user/{OWNER_USER_ID}/monetary-account/{ACCOUNT_ID}/customer-statement, I get the correct items immediately.

Suggested fixes

  • Make listing resilient for company accounts by either:
  • Auto-detecting the correct owner user id from the monetary_account_id before building the path, or
  • Exposing an overload or alternate method that accepts user_id as an explicit parameter, e.g., ExportStatementApiObject.list_for_user(user_id, monetary_account_id, ...).
  • Ensure consistent JSON shape mapping in the Python SDK. The REST response for listing is typically: {"Response": [{"CustomerStatementExport": {...}}, ...]} The Python SDK sometimes exposes "ExportStatement," while the raw API uses "CustomerStatementExport." Aligning/normalizing this in the SDK payload models would help.

Minimal repro (Python)

  • Environment:
    • Python 3.11 on macOS
    • bunq Python SDK installed from PyPI
  • Sketch:
   from bunq.sdk.context.api_context import ApiContext, ApiEnvironmentType
   from bunq.sdk.context.bunq_context import BunqContext
   from bunq.sdk.model.generated import endpoint as bunq_endpoint
   from bunq.sdk.http.api_client import ApiClient

   # Setup
   api_context = ApiContext.create(ApiEnvironmentType.PRODUCTION, "<API_KEY>", "exporter")
   BunqContext.load_api_context(api_context)

   # Enumerate accounts (includes company accounts)
   accounts = bunq_endpoint.MonetaryAccountApiObject.list().value

   # For each account:
   # resolve concrete subtype + id (e.g., account_id)
   acc_id = <ACCOUNT_ID>

   # 1) SDK list: pagination args not accepted -> TypeError
   # bunq_endpoint.ExportStatementApiObject.list(monetary_account_id=acc_id, count=50)  # raises TypeError

   # 2) SDK list without pagination: returns items for personal, empty for company-owned accounts
   res = bunq_endpoint.ExportStatementApiObject.list(monetary_account_id=acc_id).value  # empty for company

   # 3) Direct HTTP call using owner user id works
   owner_uid = <OWNER_USER_ID>
   api_client = ApiClient(api_context)  # reuse session
   raw = api_client.get(f"user/{owner_uid}/monetary-account/{acc_id}/customer-statement", {"count": "50"}, {})
   data = raw.body_bytes  # JSON -> Response list contains CustomerStatementExport items

Direct REST illustration

hunter-nl avatar Oct 08 '25 13:10 hunter-nl