taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

api-bank-wire.rst (24471B)


      1 ..
      2   This file is part of GNU TALER.
      3   Copyright (C) 2019-2025, 2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 2.1, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 
     16 .. _taler-wire-gateway-http-api:
     17 
     18 ===========================
     19 Taler Wire Gateway HTTP API
     20 ===========================
     21 
     22 This section describes the API offered by the Taler wire adapters. The API is
     23 used by the exchange to trigger transactions and query incoming transactions, as
     24 well as by the auditor to query incoming and outgoing transactions.
     25 
     26 This API is currently implemented by the Taler Demo Bank, as well as by
     27 LibEuFin.
     28 
     29 .. http:get:: /config
     30 
     31   Return the protocol version and configuration information about the bank.
     32   This specification corresponds to ``current`` protocol being version **5**.
     33 
     34   **Response:**
     35 
     36   :http:statuscode:`200 OK`:
     37     The adapter responds with a `WireConfig` object. This request should
     38     virtually always be successful.
     39 
     40   **Details:**
     41 
     42   .. ts:def:: WireConfig
     43 
     44     interface WireConfig {
     45       // Name of the API.
     46       name: "taler-wire-gateway";
     47 
     48       // libtool-style representation of the Bank protocol version, see
     49       // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
     50       // The format is "current:revision:age".
     51       version: string;
     52 
     53       // Currency used by this gateway.
     54       currency: string;
     55 
     56       // URN of the implementation (needed to interpret 'revision' in version).
     57       // @since v0, may become mandatory in the future.
     58       implementation?: string;
     59 
     60       // Whether implementation support account existence check
     61       support_account_check: boolean;
     62     }
     63 
     64 --------------
     65 Authentication
     66 --------------
     67 
     68 The bank library authenticates requests to the wire gateway via
     69 `HTTP basic auth <https://tools.ietf.org/html/rfc7617>`_.
     70 
     71 -------------------
     72 Making Transactions
     73 -------------------
     74 
     75 .. http:post:: /transfer
     76 
     77   Initiate a new wire transfer from the exchange's bank account, typically to a
     78   merchant.
     79 
     80   The exchange's bank account is not included in the request, but instead
     81   derived from the username in the ``Authorization`` header and/or the request
     82   base URL.
     83 
     84   To make the API idempotent, the client must include a nonce. Requests with
     85   the same nonce are rejected unless the request is the same.
     86 
     87   **Request:**
     88 
     89   .. ts:def:: TransferRequest
     90 
     91     interface TransferRequest {
     92       // Nonce to make the request idempotent.  Requests with the same
     93       // ``request_uid`` that differs in any of the other fields
     94       // are rejected.
     95       request_uid: HashCode;
     96 
     97       // Amount to transfer.
     98       amount: Amount;
     99 
    100       // Base URL of the exchange.  Shall be included by the bank gateway
    101       // in the appropriate section of the wire transfer details.
    102       exchange_base_url: string;
    103       
    104       // Optional additional metadata to be stored in the transaction.
    105       // Must match [a-zA-Z0-9-.:]{1, 40}
    106       // @since **v5**
    107       metadata?: string;
    108 
    109       // Wire transfer identifier chosen by the exchange,
    110       // used by the merchant to identify the Taler order(s)
    111       // associated with this wire transfer.
    112       wtid: ShortHashCode;
    113 
    114       // The recipient's account identifier as a full payto URI.
    115       credit_account: string;
    116     }
    117 
    118   **Response:**
    119 
    120   :http:statuscode:`200 OK`:
    121     The request has been correctly handled, so the funds have been transferred to
    122     the recipient's account.  The body is a `TransferResponse`.
    123   :http:statuscode:`400 Bad request`:
    124     Request malformed. The bank replies with an `ErrorDetail` object.
    125   :http:statuscode:`401 Unauthorized`:
    126     Authentication failed, likely the credentials are wrong.
    127   :http:statuscode:`404 Not found`:
    128     The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
    129   :http:statuscode:`409 Conflict`:
    130     * ``TALER_EC_BANK_TRANSFER_REQUEST_UID_REUSED``: an operation with the same ``request_uid`` but different details has been submitted before.
    131     * ``TALER_EC_BANK_TRANSFER_WTID_REUSED``: an operation with the same ``wtid`` but a different ``request_uid`` has been submitted before.
    132 
    133   **Details:**
    134 
    135   .. ts:def:: TransferResponse
    136 
    137     interface TransferResponse {
    138       // Timestamp that indicates when the wire transfer will be executed.
    139       // In cases where the wire transfer gateway is unable to know when
    140       // the wire transfer will be executed, the time at which the request
    141       // has been received and stored will be returned.
    142       // The purpose of this field is for debugging (humans trying to find
    143       // the transaction) as well as for taxation (determining which
    144       // time period a transaction belongs to).
    145       timestamp: Timestamp;
    146 
    147       // Opaque ID of the wire transfer initiation performed by the bank.
    148       // It is different from the /history endpoints row_id.
    149       row_id: SafeUint64;
    150     }
    151 
    152 .. http:get:: /transfers
    153 
    154   Return a list of transfers initiated from the exchange.
    155 
    156   The bank account of the exchange is determined via the base URL and/or the
    157   user name in the ``Authorization`` header. The transfer history
    158   might come from a "virtual" account, where multiple real bank accounts are
    159   merged into one history.
    160 
    161   Since protocol **v3**.
    162 
    163   **Request:**
    164 
    165   :query limit: *Optional.*
    166     At most return the given number of results. Negative for descending by
    167     ``row_id``, positive for ascending by ``row_id``. Defaults to ``-20``.
    168   :query offset: *Optional.*
    169     Starting ``row_id`` for :ref:`pagination <row-id-pagination>`.
    170   :query status: *Optional*.
    171     Filters by status.
    172 
    173   **Response:**
    174 
    175   :http:statuscode:`200 OK`:
    176     JSON object of type `TransferList`.
    177   :http:statuscode:`204 No content`:
    178     There are no transfers to report (under the given filter).
    179   :http:statuscode:`400 Bad request`:
    180     Request malformed.
    181   :http:statuscode:`401 Unauthorized`:
    182     Authentication failed, likely the credentials are wrong.
    183   :http:statuscode:`404 Not found`:
    184     The endpoint is wrong or the user name is unknown.
    185 
    186   **Details:**
    187 
    188   .. ts:def:: TransferList
    189 
    190     interface TransferList {
    191       // Array of initiated transfers.
    192       transfers: TransferListStatus[];
    193 
    194       // Full payto:// URI to identify the sender of funds.
    195       // This must be one of the exchange's bank accounts.
    196       // Credit account is shared by all incoming transactions
    197       // as per the nature of the request.
    198       debit_account: string;
    199     }
    200 
    201   .. ts:def:: TransferListStatus
    202 
    203     interface TransferListStatus {
    204       // Opaque ID of the wire transfer initiation performed by the bank.
    205       // It is different from the /history endpoints row_id.
    206       row_id: SafeUint64;
    207 
    208       // Current status of the transfer
    209       // pending: the transfer is in progress
    210       // transient_failure: the transfer has failed but may succeed later
    211       // permanent_failure: the transfer has failed permanently and will never appear in the outgoing history
    212       // success: the transfer has succeeded  and appears in the outgoing history
    213       status: "pending" | "transient_failure" | "permanent_failure" | "success";
    214 
    215       // Amount to transfer.
    216       amount: Amount;
    217 
    218       // The recipient's account identifier as a full payto:// URI.
    219       credit_account: string;
    220 
    221       // Timestamp that indicates when the wire transfer was executed.
    222       // In cases where the wire transfer gateway is unable to know when
    223       // the wire transfer will be executed, the time at which the request
    224       // has been received and stored will be returned.
    225       // The purpose of this field is for debugging (humans trying to find
    226       // the transaction) as well as for taxation (determining which
    227       // time period a transaction belongs to).
    228       timestamp: Timestamp;
    229     }
    230 
    231 
    232 .. http:get:: /transfers/$ROW_ID
    233 
    234   Return the status of a transfer initiated from the exchange, identified by the ``ROW_ID``.
    235 
    236   Since protocol **v3**.
    237 
    238   **Response:**
    239 
    240   :http:statuscode:`200 OK`:
    241     The transfer is known, and details are given in the `TransferStatus` response body.
    242   :http:statuscode:`400 Bad request`:
    243     Request malformed.
    244   :http:statuscode:`401 Unauthorized`:
    245     Authentication failed, likely the credentials are wrong.
    246   :http:statuscode:`404 Not found`:
    247     The transfer was not found.
    248 
    249   **Details:**
    250 
    251   .. ts:def:: TransferStatus
    252 
    253     interface TransferStatus {
    254       // Current status of the transfer
    255       // pending: the transfer is in progress
    256       // transient_failure: the transfer has failed but may succeed later
    257       // permanent_failure: the transfer has failed permanently and will never appear in the outgoing history
    258       // success: the transfer has succeeded  and appears in the outgoing history
    259       status: "pending" | "transient_failure" | "permanent_failure" | "success";
    260 
    261       // Optional unstructured messages about the transfer's status. Can be used to document the reasons for failure or the state of progress.
    262       status_msg?: string;
    263 
    264       // Amount to transfer.
    265       amount: Amount;
    266 
    267       // Base URL of the exchange.  Shall be included by the bank gateway
    268       // in the appropriate section of the wire transfer details.
    269       exchange_base_url: string;
    270       
    271       // Optional additional metadata to be stored in the transaction.
    272       // @since **v5**
    273       metadata?: string;
    274 
    275       // Wire transfer identifier chosen by the exchange,
    276       // used by the merchant to identify the Taler order(s)
    277       // associated with this wire transfer.
    278       wtid: ShortHashCode;
    279 
    280       // The recipient's account identifier as a full payto URI.
    281       credit_account: string;
    282 
    283       // Timestamp that indicates when the wire transfer was executed.
    284       // In cases where the wire transfer gateway is unable to know when
    285       // the wire transfer will be executed, the time at which the request
    286       // has been received and stored will be returned.
    287       // The purpose of this field is for debugging (humans trying to find
    288       // the transaction) as well as for taxation (determining which
    289       // time period a transaction belongs to).
    290       timestamp: Timestamp;
    291     }
    292 
    293 --------------------------------
    294 Querying the transaction history
    295 --------------------------------
    296 
    297 The exchange's bank account is derived from the username in the
    298 ``Authorization`` header and/or the request's base URL. In fact, the
    299 transaction history may come from a "virtual" account, where several real bank
    300 accounts are merged into a single history.
    301 
    302 .. http:get:: /history/incoming
    303 
    304   Return a list of transactions made from or to the exchange.
    305 
    306   Incoming transactions must contain a valid reserve public key.  If a bank
    307   transaction does not conform to the right syntax, the wire gateway must not
    308   report it to the exchange, and send funds back to the sender if possible.
    309 
    310   **Request:**
    311 
    312   :query limit: *Optional.*
    313     At most return the given number of results. Negative for descending by ``row_id``, positive for ascending by ``row_id``. Defaults to ``-20``. Since protocol **v2**.
    314   :query offset: *Optional.*
    315     Starting ``row_id`` for :ref:`pagination <row-id-pagination>`. Since protocol **v2**.
    316   :query timeout_ms: *Optional.*
    317     Timeout in milliseconds, for :ref:`long-polling <long-polling>`, to wait for at least one element to be shown. Only useful if *limit* is positive. Since protocol **v2**.
    318   :query delta: *Optional.*
    319     Deprecated in protocol **v2**. Use *limit* instead.
    320   :query start: *Optional.*
    321     Deprecated in protocol **v2**. Use *offset* instead.
    322   :query long_poll_ms: *Optional.*
    323     Deprecated in protocol **v2**. Use *timeout_ms* instead.
    324 
    325   **Response:**
    326 
    327   :http:statuscode:`200 OK`:
    328      JSON object of type `IncomingHistory`.
    329   :http:statuscode:`204 No content`:
    330     There are not transactions to report (under the given filter).
    331   :http:statuscode:`400 Bad request`:
    332      Request malformed. The bank replies with an `ErrorDetail` object.
    333   :http:statuscode:`401 Unauthorized`:
    334      Authentication failed, likely the credentials are wrong.
    335   :http:statuscode:`404 Not found`:
    336      The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
    337 
    338   **Details:**
    339 
    340   .. ts:def:: IncomingHistory
    341 
    342     interface IncomingHistory {
    343       // Array of incoming transactions.
    344       incoming_transactions: IncomingBankTransaction[];
    345 
    346       // Full payto URI to identify the receiver of funds.
    347       // This must be one of the exchange's bank accounts.
    348       // Credit account is shared by all incoming transactions
    349       // as per the nature of the request.
    350       credit_account: string;
    351     }
    352 
    353   .. ts:def:: IncomingBankTransaction
    354 
    355     // Union discriminated by the "type" field.
    356     type IncomingBankTransaction =
    357     | IncomingKycAuthTransaction
    358     | IncomingReserveTransaction
    359     | IncomingWadTransaction;
    360 
    361   .. ts:def:: IncomingKycAuthTransaction
    362 
    363     // Since protocol **v1**.
    364     interface IncomingKycAuthTransaction {
    365       type: "KYCAUTH";
    366 
    367       // Opaque identifier of the returned record.
    368       row_id: SafeUint64;
    369 
    370       // Date of the transaction.
    371       date: Timestamp;
    372 
    373       // Amount received before credit_fee.
    374       amount: Amount;
    375 
    376       // Fee paid by the creditor.
    377       // If not null, creditor actually received amount - credit_fee
    378       // @since **v3**
    379       credit_fee?: Amount;
    380 
    381       // Full payto URI to identify the sender of funds.
    382       debit_account: string;
    383 
    384       // The account public key extracted from the transaction details.
    385       account_pub: EddsaPublicKey;
    386 
    387       // The authorization public key used for mapping
    388       // @since **v5**
    389       authorization_pub?: EddsaPublicKey;
    390 
    391       // Signature of the account public key using the authorization private key
    392       // @since **v5**
    393       authorization_sig?: EddsaSignature;
    394     }
    395 
    396   .. ts:def:: IncomingReserveTransaction
    397 
    398     interface IncomingReserveTransaction {
    399       type: "RESERVE";
    400 
    401       // Opaque identifier of the returned record.
    402       row_id: SafeUint64;
    403 
    404       // Date of the transaction.
    405       date: Timestamp;
    406 
    407       // Amount received before credit_fee.
    408       amount: Amount;
    409 
    410       // Fee payed by the creditor.
    411       // If not null, creditor actually received amount - 
    412       // @since **v3**
    413       credit_fee?: Amount;
    414 
    415       // Full payto URI to identify the sender of funds.
    416       debit_account: string;
    417 
    418       // The reserve public key extracted from the transaction details.
    419       reserve_pub: EddsaPublicKey;
    420 
    421       // The authorization public key used for mapping
    422       // @since **v5**
    423       authorization_pub?: EddsaPublicKey;
    424 
    425       // Signature of the reserve public key using the authorization private key
    426       // @since **v5**
    427       authorization_sig?: EddsaSignature;
    428     }
    429 
    430   .. ts:def:: IncomingWadTransaction
    431 
    432     interface IncomingWadTransaction {
    433       type: "WAD";
    434 
    435       // Opaque identifier of the returned record.
    436       row_id: SafeUint64;
    437 
    438       // Date of the transaction.
    439       date: Timestamp;
    440 
    441       // Amount received before credit_fee.
    442       amount: Amount;
    443 
    444       // Fee payed by the creditor.
    445       // If not null, creditor actually received amount - credit_fee
    446       // @since **v3**
    447       credit_fee?: Amount;
    448 
    449       // Full payto URI to identify the sender of funds.
    450       debit_account: string;
    451 
    452       // Base URL of the exchange that originated the wad.
    453       origin_exchange_url: string;
    454 
    455       // The reserve public key extracted from the transaction details.
    456       wad_id: WadId;
    457     }
    458 
    459 
    460 .. http:get:: /history/outgoing
    461 
    462   Return a list of transactions made by the exchange, typically to a merchant.
    463 
    464   **Request:**
    465 
    466   :query limit: *Optional.*
    467     At most return the given number of results. Negative for descending by ``row_id``, positive for ascending by ``row_id``. Defaults to ``-20``. Since protocol **v2**.
    468   :query offset: *Optional.*
    469     Starting ``row_id`` for :ref:`pagination <row-id-pagination>`. Since protocol **v2**.
    470   :query timeout_ms: *Optional.*
    471     Timeout in milliseconds, for :ref:`long-polling <long-polling>`, to wait for at least one element to be shown. Only useful if *limit* is positive. Since protocol **v2**.
    472   :query delta: *Optional.*
    473     Deprecated in protocol **v2**. Use *limit* instead.
    474   :query start: *Optional.*
    475     Deprecated in protocol **v2**. Use *offset* instead.
    476   :query long_poll_ms: *Optional.*
    477     Deprecated in protocol **v2**. Use *timeout_ms* instead.
    478 
    479   **Response:**
    480 
    481   :http:statuscode:`200 OK`:
    482     JSON object of type `OutgoingHistory`.
    483   :http:statuscode:`204 No content`:
    484     There are not transactions to report (under the given filter).
    485   :http:statuscode:`400 Bad request`:
    486     Request malformed. The bank replies with an `ErrorDetail` object.
    487   :http:statuscode:`401 Unauthorized`:
    488     Authentication failed, likely the credentials are wrong.
    489   :http:statuscode:`404 Not found`:
    490     The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
    491 
    492   **Details:**
    493 
    494   .. ts:def:: OutgoingHistory
    495 
    496     interface OutgoingHistory {
    497       // Array of outgoing transactions.
    498       outgoing_transactions: OutgoingBankTransaction[];
    499 
    500       // Full payto URI to identify the sender of funds.
    501       // This must be one of the exchange's bank accounts.
    502       // Credit account is shared by all incoming transactions
    503       // as per the nature of the request.
    504       debit_account: string;
    505     }
    506 
    507   .. ts:def:: OutgoingBankTransaction
    508 
    509     interface OutgoingBankTransaction {
    510       // Opaque identifier of the returned record.
    511       row_id: SafeUint64;
    512 
    513       // Date of the transaction.
    514       date: Timestamp;
    515 
    516       // Amount transferred.
    517       amount: Amount;
    518 
    519       // Fee paid by the debtor.
    520       // If not null, debtor actually paid amount + debit_fee
    521       // @since **v3**
    522       debit_fee?: Amount;
    523 
    524       // Full payto URI to identify the receiver of funds.
    525       credit_account: string;
    526 
    527       // The wire transfer ID in the outgoing transaction.
    528       wtid: ShortHashCode;
    529 
    530       // Base URL of the exchange.
    531       exchange_base_url: string;
    532       
    533       // Optional additional metadata.
    534       // @since **v5**
    535       metadata?: string;
    536     }
    537 
    538 
    539 -----------------
    540 Wire Account APIs
    541 -----------------
    542 
    543 .. http:get:: /account/check
    544 
    545   Check account existence.
    546 
    547   Since protocol **v4**.
    548 
    549   **Request:**
    550 
    551   :query account:
    552     Payto URI of the account.
    553 
    554   **Response:**
    555 
    556   :http:statuscode:`200 OK`:
    557     JSON object of type `AccountInfo`.
    558   :http:statuscode:`400 Bad request`:
    559     Request malformed. The bank replies with an `ErrorDetail` object.
    560   :http:statuscode:`401 Unauthorized`:
    561     Authentication failed, likely the credentials are wrong.
    562   :http:statuscode:`404 Not found`:
    563     * ``TALER_EC_BANK_UNKNOWN_ACCOUNT``: unknown account.
    564   :http:statuscode:`501 Not Implemented`:
    565     This server does not support account check.
    566 
    567   **Details:**
    568 
    569   .. ts:def:: AccountInfo
    570 
    571     interface AccountInfo {
    572     }
    573 
    574 -----------------------
    575 Wire Transfer Test APIs
    576 -----------------------
    577 
    578 Endpoints in this section are only used for integration tests and never
    579 exposed by bank gateways in production.
    580 
    581 .. _twg-admin-add-incoming:
    582 
    583 .. http:post:: /admin/add-incoming
    584 
    585   Simulate a transfer from a customer to the exchange.  This API is *not*
    586   idempotent since it's only used in testing.
    587 
    588   **Request:**
    589 
    590   .. ts:def:: AddIncomingRequest
    591 
    592     interface AddIncomingRequest {
    593       // Amount to transfer.
    594       amount: Amount;
    595 
    596       // Reserve public key that is included in the wire transfer details
    597       // to identify the reserve that is being topped up.
    598       reserve_pub: EddsaPublicKey;
    599 
    600       // Account (as full payto URI) that makes the wire transfer to the exchange.
    601       // Usually this account must be created by the test harness before this
    602       // API is used. An exception is the "fakebank", where any debit account
    603       // can be specified, as it is automatically created.
    604       debit_account: string;
    605     }
    606 
    607   **Response:**
    608 
    609   :http:statuscode:`200 OK`:
    610     The request has been correctly handled, so the funds have been transferred to
    611     the recipient's account.  The body is a `AddIncomingResponse`.
    612   :http:statuscode:`400 Bad request`:
    613     The request is malformed. The bank replies with an `ErrorDetail` object.
    614   :http:statuscode:`401 Unauthorized`:
    615     Authentication failed, likely the credentials are wrong.
    616   :http:statuscode:`404 Not found`:
    617     The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
    618   :http:statuscode:`409 Conflict`:
    619     The 'reserve_pub' argument was used previously in another transfer, and the specification mandates that reserve public keys must not be reused.
    620 
    621   **Details:**
    622 
    623   .. ts:def:: AddIncomingResponse
    624 
    625     interface AddIncomingResponse {
    626       // Timestamp that indicates when the wire transfer will be executed.
    627       // In cases where the wire transfer gateway is unable to know when
    628       // the wire transfer will be executed, the time at which the request
    629       // has been received and stored will be returned.
    630       // The purpose of this field is for debugging (humans trying to find
    631       // the transaction) as well as for taxation (determining which
    632       // time period a transaction belongs to).
    633       timestamp: Timestamp;
    634 
    635       // Opaque ID of the wire transfer initiation performed by the bank.
    636       // It is different from the /history endpoints row_id.
    637       row_id: SafeUint64;
    638     }
    639 
    640 
    641 
    642 .. _twg-admin-add-kycauth:
    643 
    644 .. http:post:: /admin/add-kycauth
    645 
    646   Simulate a transfer from a customer to the exchange.  This API is *not*
    647   idempotent since it's only used in testing.
    648 
    649   **Request:**
    650 
    651   .. ts:def:: AddKycauthRequest
    652 
    653     interface AddKycauthRequest {
    654       // Amount to transfer.
    655       amount: Amount;
    656 
    657       // Account public key that is included in the wire transfer details
    658       // to associate this key with the originating bank account.
    659       account_pub: EddsaPublicKey;
    660 
    661       // Account (as full payto URI) that makes the wire transfer to the exchange.
    662       // Usually this account must be created by the test harness before this
    663       // API is used. An exception is the "fakebank", where any debit account
    664       // can be specified, as it is automatically created.
    665       debit_account: string;
    666     }
    667 
    668   **Response:**
    669 
    670   :http:statuscode:`200 OK`:
    671     The request has been correctly handled, so the funds have been transferred to
    672     the recipient's account.  The body is a `AddKycauthResponse`.
    673   :http:statuscode:`400 Bad request`:
    674     The request is malformed. The bank replies with an `ErrorDetail` object.
    675   :http:statuscode:`401 Unauthorized`:
    676     Authentication failed, likely the credentials are wrong.
    677   :http:statuscode:`404 Not found`:
    678     The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
    679 
    680   **Details:**
    681 
    682   .. ts:def:: AddKycauthResponse
    683 
    684     interface AddKycauthResponse {
    685       // Timestamp that indicates when the wire transfer will be executed.
    686       // In cases where the wire transfer gateway is unable to know when
    687       // the wire transfer will be executed, the time at which the request
    688       // has been received and stored will be returned.
    689       // The purpose of this field is for debugging (humans trying to find
    690       // the transaction) as well as for taxation (determining which
    691       // time period a transaction belongs to).
    692       timestamp: Timestamp;
    693 
    694       // Opaque ID of the wire transfer initiation performed by the bank.
    695       // It is different from the /history endpoints row_id.
    696       row_id: SafeUint64;
    697     }
    698 
    699 
    700 Security Considerations
    701 =======================
    702 
    703 For implementors:
    704 
    705 * The withdrawal operation ID must contain enough entropy to be unguessable.
    706 
    707 Design:
    708 
    709 * The user must complete the 2FA step of the withdrawal in the context of their banking
    710   app or online banking Website.
    711   We explicitly reject any design where the user would have to enter a confirmation code
    712   they get from their bank in the context of the wallet, as this would teach and normalize
    713   bad security habits.