Wealth Reader Cards (real time) API

Real-time card expense synchronization from the Open Sync mobile app: per-customer employee pre-registration, signed webhooks (card_transaction.created / card_enrollment.confirmed), and REST query / backfill.

Operations 4

POST /cards/webhook/ Register or rotate the real-time cards webhook #
POST /cards/enrollments/ Pre-register the email of an employee #
GET /cards/enrollments/ Check the status of an enrollment #
GET /cards/transactions/ Query / backfill real-time card transactions #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/wealth-reader-cards-real-time-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

wealth-reader-cards-real-time-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  description: Regulatory APIs based on PSD2 provide access to certain financial information such as bank account balances and transactions.
  version: 8.1.7
  title: Wealth Reader Cards (real time) API
  contact:
    email: info@wealthreader.com
servers:
- description: Production Server
  url: https://api.wealthreader.com/
tags:
- name: Cards (real time)
  description: 'Real-time card expense synchronization from the Open Sync mobile app: per-customer employee pre-registration, signed webhooks (card_transaction.created / card_enrollment.confirmed), and REST query / backfill.'
paths:
  /cards/webhook/:
    post:
      tags:
      - Cards (real time)
      summary: Register or rotate the real-time cards webhook
      description: 'Creates or updates the webhook URL of the customer for the card_transaction.created and card_enrollment.confirmed events (see the cards-webhook-delivery schema for the delivery format and signature). On first setup, or when rotate_secret is true, a new webhook_secret (64 hex characters) is generated and returned once; in any other case webhook_secret comes back as null in the response and cannot be retrieved again. webhook_url must always be https:// and must resolve to a publicly routable host: localhost, private, loopback, link-local (including the cloud metadata address), CGNAT, multicast and reserved addresses are rejected, in any notation (hexadecimal, decimal, octal, short dotted or IPv4-mapped IPv6), and so is a hostname that does not resolve at all. The same check runs again right before every delivery, not only at registration: if the host is repointed at an internal address afterwards (DNS rebinding) the delivery is closed as failed with response_excerpt "blocked_host". Sending null in webhook_url disables webhooks for that customer; omitting the field leaves the stored URL untouched, which is how the secret is rotated without changing the URL.'
      operationId: cardsWebhookRegister
      x-webhook-delivery:
        description: Body that Wealthreader POSTs to webhook_url for each event of this customer. Documented here so the delivery schema stays reachable from the operation that registers the webhook.
        schema:
          $ref: '#/components/schemas/cards-webhook-delivery'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - api_key
              properties:
                api_key:
                  type: string
                  description: API key of the customer.
                  example: a1b2c3d4
                webhook_url:
                  type:
                  - string
                  - 'null'
                  description: https:// URL that will receive the events, on a publicly routable host that resolves in DNS. null disables webhooks; omitting the field leaves the stored URL unchanged.
                  example: https://cliente.example.com/webhooks/wealthreader-cards
                rotate_secret:
                  type: boolean
                  default: false
                  description: When true, generates and returns a new webhook_secret.
              example:
                api_key: a1b2c3d4
                webhook_url: https://cliente.example.com/webhooks/wealthreader-cards
                rotate_secret: false
      responses:
        '200':
          description: Webhook registered or updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  payload:
                    type: object
                    properties:
                      webhook_url:
                        type:
                        - string
                        - 'null'
                      webhook_secret:
                        type:
                        - string
                        - 'null'
                        description: Only filled in on creation or rotation; null in any other case.
                      active:
                        type: boolean
              example:
                success: true
                payload:
                  webhook_url: https://cliente.example.com/webhooks/wealthreader-cards
                  webhook_secret: 9f1c2b3a4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8
                  active: true
        '400':
          description: Invalid webhook_url
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  code:
                    type: string
                    example: invalid_webhook_url
                  message:
                    type: string
        '401':
          description: Invalid or inactive api_key
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  code:
                    type: string
                    example: unauthorized
                  message:
                    type: string
  /cards/enrollments/:
    post:
      tags:
      - Cards (real time)
      summary: Pre-register the email of an employee
      description: 'Creates an enrollment request in pending status with a short expiry (ttl_minutes, 20 by default, between 1 and 60) for the employee to confirm by opening the mobile app and entering that email (POST /user-sync-validation/, no contract change for the app). It is idempotent: repeating the call for the same (api_key, email) while it is still pending and not expired returns the same request. If the email is already linked to the calling customer, it returns status "active" directly. If it is already linked to a different customer, it returns 409. Rate limit: at most 60 calls to this endpoint per api_key every 60 seconds, counting every attempt and not only the ones that create a row, checked before anything else so the answers that create nothing (200 already active, 409 linked to another customer, 400) cannot be walked as an enumeration oracle. Exceeding it returns 429 with code rate_limited.'
      operationId: cardsEnrollmentsCreate
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - api_key
              - email
              properties:
                api_key:
                  type: string
                  description: API key of the customer.
                  example: a1b2c3d4
                email:
                  type: string
                  format: email
                  description: Email of the employee to pre-register.
                  example: empleado@cliente.com
                ttl_minutes:
                  type: integer
                  default: 20
                  minimum: 1
                  maximum: 60
                  description: Minutes the request stays valid before expiring.
              example:
                api_key: a1b2c3d4
                email: empleado@cliente.com
                ttl_minutes: 20
      responses:
        '200':
          description: Request created, reused or already active
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/cards-enrollment-response'
              example:
                success: true
                payload:
                  enrollment_id: 0f3a9c7b1d2e4f5a6b7c8d9e0f1a2b3c
                  status: pending
                  email: empleado@cliente.com
                  expires_at: '2026-07-11T14:22:11+02:00'
                  confirmed_at: null
                  user_id: null
        '400':
          description: Invalid parameters (email or ttl_minutes)
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  code:
                    type: string
                    example: invalid_email
                  message:
                    type: string
        '401':
          description: Invalid or inactive api_key
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  code:
                    type: string
                    example: unauthorized
                  message:
                    type: string
        '409':
          description: The email is already linked to another customer
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  code:
                    type: string
                    example: email_linked_to_other_client
                  message:
                    type: string
        '429':
          description: Too many calls to this endpoint for this api_key in the last 60 seconds
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  code:
                    type: string
                    example: rate_limited
                  message:
                    type: string
    get:
      tags:
      - Cards (real time)
      summary: Check the status of an enrollment
      description: Read-only status of an enrollment request. It has no side effects on card users, unlike POST /user-sync-validation/, which does confirm. The only write allowed is lazily marking a pending enrollment whose expiry date has already passed as expired.
      operationId: cardsEnrollmentsStatus
      parameters:
      - name: api_key
        in: query
        required: true
        schema:
          type: string
        description: API key of the customer. Note it travels in the query string, so it ends up in access logs and intermediary proxies.
        example: a1b2c3d4
      - name: enrollment_id
        in: query
        required: true
        schema:
          type: string
        description: Identifier returned by POST /cards/enrollments/.
        example: 0f3a9c7b1d2e4f5a6b7c8d9e0f1a2b3c
      responses:
        '200':
          description: Enrollment status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/cards-enrollment-response'
              example:
                success: true
                payload:
                  enrollment_id: 0f3a9c7b1d2e4f5a6b7c8d9e0f1a2b3c
                  status: active
                  email: empleado@cliente.com
                  expires_at: '2026-07-11T14:22:11+02:00'
                  confirmed_at: '2026-07-11T14:10:03+02:00'
                  user_id: e9f4c2a1b3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7
        '401':
          description: Invalid or inactive api_key
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  code:
                    type: string
                    example: unauthorized
                  message:
                    type: string
        '404':
          description: The enrollment does not exist or does not belong to this api_key
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  code:
                    type: string
                    example: not_found
                  message:
                    type: string
  /cards/transactions/:
    get:
      tags:
      - Cards (real time)
      summary: Query / backfill real-time card transactions
      description: Returns the real-time card transactions received for the employees linked to this api_key, ordered by ascending id. Meant both for periodic backfill (poll with date_from/date_to and paginate with since_id) and for one-off queries. This is the same transaction object carried by the card_transaction.created webhook.
      operationId: cardsTransactionsList
      parameters:
      - name: api_key
        in: query
        required: true
        schema:
          type: string
        description: API key of the customer. Note it travels in the query string, so it ends up in access logs and intermediary proxies.
        example: a1b2c3d4
      - name: date_from
        in: query
        required: false
        schema:
          type: string
          format: date
        description: 'YYYY-MM-DD, on the operation date. Default: today minus 3 days.'
        example: '2026-07-01'
      - name: date_to
        in: query
        required: false
        schema:
          type: string
          format: date
        description: 'YYYY-MM-DD, on the operation date. Default: today.'
        example: '2026-07-11'
      - name: email
        in: query
        required: false
        schema:
          type: string
          format: email
        description: Filters by the email of the employee.
        example: empleado@cliente.com
      - name: since_id
        in: query
        required: false
        schema:
          type: integer
        description: Exclusive cursor on the transaction id, for pagination.
        example: 216
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          default: 500
          maximum: 1000
        description: Maximum number of transactions to return (500 by default, 1000 max).
        example: 500
      responses:
        '200':
          description: Transactions found
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  payload:
                    type: object
                    properties:
                      transactions:
                        type: array
                        items:
                          $ref: '#/components/schemas/cards-transaction'
                      next_since_id:
                        type:
                        - integer
                        - 'null'
                        description: Last id returned when limit was reached; null when there are no more pages.
              example:
                success: true
                payload:
                  transactions:
                  - id: 217
                    payment_id: 550e8400-e29b-41d4-a716-446655440000
                    user_id: e9f4c2a1b3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7
                    email: empleado@cliente.com
                    card_last4: '1234'
                    entity_code: bbva
                    operation_date: '2026-07-11'
                    value_date: '2026-07-11'
                    amount: -57.6
                    currency: null
                    description: SAKE RESTAURANTE
                    merchant: SAKE RESTAURANTE
                    category: Other
                    gps:
                      lat: 39.4661559
                      lon: -0.3679207
                    received_at: '2026-07-11T14:02:11+02:00'
                  next_since_id: null
        '400':
          description: Invalid parameters (dates, since_id, limit or email)
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  code:
                    type: string
                    example: invalid_date_from
                  message:
                    type: string
        '401':
          description: Invalid or inactive api_key
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  code:
                    type: string
                    example: unauthorized
                  message:
                    type: string
components:
  schemas:
    cards-transaction:
      type: object
      description: Real-time card transaction object. Same format returned by GET /cards/transactions/ and sent in the card_transaction.created webhook.
      properties:
        id:
          type: integer
        payment_id:
          type: string
        user_id:
          type: string
          description: Card user uuid.
        email:
          type: string
          format: email
        card_last4:
          type:
          - string
          - 'null'
        entity_code:
          type:
          - string
          - 'null'
        operation_date:
          type: string
          format: date
        value_date:
          type: string
          format: date
        amount:
          type: number
          format: float
        currency:
          type:
          - string
          - 'null'
          description: Always null today; the app does not send a currency.
        description:
          type: string
        merchant:
          type: string
        category:
          type: string
        gps:
          type:
          - object
          - 'null'
          properties:
            lat:
              type: number
              format: float
            lon:
              type: number
              format: float
        received_at:
          type: string
          format: date-time
    cards-webhook-delivery:
      type: object
      description: 'Body sent by Wealthreader to the webhook_url of the customer (POST /cards/webhook/) for the card_transaction.created and card_enrollment.confirmed events. Headers: Content-Type: application/json, User-Agent: Wealthreader-Cards/1.0, Origin: https://api.wealthreader.com, X-WR-Event: <event>, X-WR-Delivery: <delivery_id>, X-WR-Signature: sha256=<hex of hmac_sha256(raw_body, webhook_secret)>. The customer must verify X-WR-Signature with a constant-time comparison (hash_equals or equivalent) before trusting the body. Success = HTTP 2xx; otherwise Wealthreader retries with backoff 1 min, 5 min, 30 min, 2 h and 24 h, resending the exact same body, and after the last failed attempt the delivery is marked failed for good. If the secret is rotated while retries are still pending, those retries are signed with the new secret.

        '
      required:
      - event
      - delivery_id
      - sent_at
      - api_key
      - data
      properties:
        event:
          type: string
          enum:
          - card_transaction.created
          - card_enrollment.confirmed
        delivery_id:
          type: string
          description: 32 hex characters. Matches the X-WR-Delivery header.
        sent_at:
          type: string
          format: date-time
        api_key:
          type: string
        data:
          oneOf:
          - $ref: '#/components/schemas/cards-transaction'
          - $ref: '#/components/schemas/cards-enrollment'
          description: cards-transaction for card_transaction.created; cards-enrollment (in active status) for card_enrollment.confirmed.
      example:
        event: card_transaction.created
        delivery_id: 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d
        sent_at: '2026-07-11T14:02:12+02:00'
        api_key: a1b2c3d4
        data:
          id: 217
          payment_id: 550e8400-e29b-41d4-a716-446655440000
          user_id: e9f4c2a1b3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7
          email: empleado@cliente.com
          card_last4: '1234'
          entity_code: bbva
          operation_date: '2026-07-11'
          value_date: '2026-07-11'
          amount: -57.6
          currency: null
          description: SAKE RESTAURANTE
          merchant: SAKE RESTAURANTE
          category: Other
          gps:
            lat: 39.4661559
            lon: -0.3679207
          received_at: '2026-07-11T14:02:11+02:00'
    cards-enrollment:
      type: object
      description: Enrollment object. Same format returned inside the payload of POST and GET /cards/enrollments/ and sent as data in the card_enrollment.confirmed webhook.
      properties:
        enrollment_id:
          type:
          - string
          - 'null'
          description: 32 hex characters. Identifier returned by POST /cards/enrollments/.
        status:
          type: string
          enum:
          - pending
          - active
          - expired
          - error
        email:
          type: string
          format: email
        expires_at:
          type:
          - string
          - 'null'
          format: date-time
        confirmed_at:
          type:
          - string
          - 'null'
          format: date-time
        user_id:
          type:
          - string
          - 'null'
          description: Card user uuid once confirmed; null while it is pending.
    cards-enrollment-response:
      type: object
      properties:
        success:
          type: boolean
        payload:
          $ref: '#/components/schemas/cards-enrollment'