Skip to content

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

@hunter-nl

Description

@hunter-nl

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] 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions