1.12. The Donau RESTful API

The API specified here follows the general conventions for all details not specified in the individual requests. The glossary defines all specific terms used in this section.

1.12.1. API Overview

This is intended to provide a quick overview of the whole REST API. For a more detailed view of the protocol, see the protocol specification.

The chapters group the families of requests frequently encountered when using the Donau API:

  • Status information: get the public signing keys of the Donau, the donation unit key, the Donaus config or some entropy

  • Issue receipts: For use by charities: Issue receipts with blinded unique donor ids from a donor.

  • Donation statement: Summarizes the donation receipts to the donation statement signature which is made over the total yearly donation amount,

    the year and the hash of taxid+salt. Provides an API to get the donation statement signature.

  • Charity administration and status information:

    • For use by administrators to add/modify a charity

    • For use by charities to get their remaining donation volume

1.12.2. Terms of service API

These APIs allow clients to obtain the terms of service and the privacy policy of a service.

GET /terms

Get the terms of service of the service. The endpoint will consider the “Accept” and “Accept-Language” and “Accept-Encoding” headers when generating a response. Specifically, it will try to find a response with an acceptable mime-type, then pick the version in the most preferred language of the user, and finally apply compression if that is allowed by the client and deemed beneficial.

The endpoint will set an “Etag”, and subsequent requests of the same client should provide the tag in an “If-None-Match” header to detect if the terms of service have changed. If not, a “304 Not Modified” response will be returned. Note that the “304 Not Modified” will also be returned if the client changed the “Accept-Language” or “Accept-Encoding” header. Thus, if the client would like to download the resource in a different language or format, the “If-None-Match” header must be omitted.

If the “Etag” is missing, the client should not cache the response and instead prompt the user again at the next opportunity. This is usually only the case if the terms of service were not configured correctly.

The “Etag” is generated from the first 256 bits of the SHA-512 hash over the terms and encoded in Crockford base-32. However, this behavior is not normative and clients MUST NOT rely on it.

A “Taler-Terms-Version” header is generated to indicate the legal version of the terms. This header will change whenever something legally changed in the terms of service and the user must review and accept the terms of service again. If the “Taler-Terms-Version” is identical to one that the user has already accepted, there is no need for the user to review the terms again.

When returning a full response (not a “304 Not Modified”), the server should also include a “Avail-Languages” header which includes a comma-separated list of the languages in which the terms of service are available in (see availability hints specification). Clients can use this to generate a language switcher for users that may not have expressed a proper language preference.

Response:

200 OK:

The body is the terms of service in the requested encoding and language.

501 Not Implemented:

The exchange lacks a valid terms of service configuration. A human-readable error message is returned. Wallets should not require the human to accept any terms of service (and do not need to show this message).

GET /privacy

Get the privacy policy of the service. Behaves the same way as The “/terms” endpoint, except that it returns the privacy policy instead of the terms of service.

Response:

200 OK:

The body is the privacy policy in the requested encoding and language.

501 Not Implemented:

The exchange lacks a valid terms of service configuration. A human-readable error message is returned. Wallets should not require the human to accept any terms of service (and do not need to show this message).

1.12.3. Donau public keys and status information

This API is used by donors and charities to obtain global information about the Donau, such as online signing keys and available donation units. This is typically the first call any Donau client makes, as it returns information required to process all of the other interactions with the Donau. The returned information is secured by signature(s) from the Donau, especially the long-term offline signing key of the Donau, which clients should cache.

GET /keys

Get a list of all donation units keys offered by the Donau, as well as the Donau’s current online signing key (used for donation statements).

Request:

Response:

200 OK:

The Donau responds with a DonauKeysResponse object. This request should virtually always be successful. It only fails if the Donau is misconfigured.

Details:

interface DonauKeysResponse {
  // libtool-style representation of the Donau protocol version, see
  // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
  // The format is "current:revision:age".
  version: string;

  // Financial domain this Donau operates for.
  domain: string;

  // The Donau's base URL.
  base_url: string;

  // The Donau's currency.
  currency: string;

  // How many digits should the amounts be rendered
  // with by default. Small capitals should
  // be used to render fractions beyond the number
  // given here (like on gas stations).
  currency_fraction_digits: Integer;

  // Donation Units offered by this Donau
  donation_units: DonationUnitKeyGroup[];

  // The Donau's signing keys.
  signkeys: SignKey[];

}
type DonationUnitKeyGroup =
  | DonationUnitKeyGroupRsa
  | DonationUnitKeyGroupCs;
interface DonationUnitKeyGroupRsa extends DonationUnitKeyGroupCommon {
  cipher: "RSA";

  donation_units: ({
    rsa_pub: RsaPublicKey;
  } & DonationUnitKeyCommon)[];
}
interface DonationUnitKeyGroupCs extends DonationUnitKeyGroupCommon {
  cipher: "CS";

  donation_units: ({
    cs_pub: Cs25519Point;
  } & DonationUnitKeyCommon)[];
}
// Common attributes for all donation unit groups
interface DonationUnitKeyGroupCommon {
  // How much was donated based on this donation receipt.
  value: Amount;

}
interface DonationUnitKeyCommon {

  // For which year is this donation unit key valid.
  year: Integer;

  // Set to 'true' if the Donau somehow "lost" the private key. The donation unit was not
  // revoked, but still cannot be used to withdraw receipts at this time (theoretically,
  // the private key could be recovered in the future; receipts signed with the private key
  // remain valid).
  lost?: boolean;
}
type DonationUnitKey =
  | RsaDonationUnitKey
  | CSDonationUnitKey;
interface RsaDonationUnitKey {
  cipher: "RSA";

  // RSA public key
  rsa_public_key: RsaPublicKey;
}
interface CSDonationUnitKey {
  cipher: "CS";

  // Public key of the donation unit.
  cs_public_key: Cs25519Point;

}

A signing key in the signkeys list is a JSON object with the following fields:

interface SignKey {
  // The actual Donau's EdDSA signing public key.
  key: EddsaPublicKey;

  // Initial validity date for the signing key.
  year: Integer;

}

Note

Both the individual donation units and the donation units list is signed, allowing customers to prove that they received an inconsistent list.

GET /seed

Return an entropy seed. The Donau will return a high-entropy value that will differ for every call. The response is NOT in JSON, but simply high-entropy binary data in the HTTP body. This API should be used by wallets to guard themselves against running on low-entropy (bad PRNG) hardware. Naturally, the entropy returned MUST be mixed with locally generated entropy.

GET /config

Return the protocol version, financial domain and currency supported by this Donau backend.

Response:

200 OK:

The body is a DonauVersionResponse.

interface DonauVersionResponse {
  // libtool-style representation of the Donau protocol version, see
  // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
  // The format is "current:revision:age".
  version: string;

  // Name of the protocol.
  name: "taler-donau";

  // Currency supported by this Donau.
  currency: string;

  // Financial domain by this Donau.
  domain: string;

}

1.12.4. Issue receipts

Inspired by the Taler exchange Withdrawal.

This API is used to obtain valid, attested donation receipts from the Donau. Use the charity GET route to see the remaining donation volume for the current year.

1.12.4.1. CSR Issue

POST /csr-issue

Obtain donau-side input values in preparation for a issue receipt step for certain donation unit cipher types, specifically at this point for Clause-Schnorr blind signatures. This API is used by the donor.

Request: The request body must be a IssuePrepareRequest object.

Response:

200 OK:

The request was successful, and the response is a IssuePrepareResponse. Note that repeating exactly the same request will again yield the same response (assuming none of the donation unit is expired).

404 Not found:

The donation unit key is not known to the donau.

410 Gone:

The requested donation unit key is not yet or no longer valid. It either before the validity year, past the year or was revoked. The response is a DonationUnitExpiredMessage. Clients must evaluate the error code provided to understand which of the cases this is and handle it accordingly.

Details:

interface IssuePrepareRequest {

  // Nonce to be used by the donau to derive
  // its private inputs from. Must not have ever
  // been used before.
  nonce: CSNonce;

  // Hash of the public key of the donation unit
  // the request relates to.
  donation_unit_pub_hash: HashCode;

}
type IssuePrepareResponse =
  | DonauIssueValue;
type DonauIssueValue =
  | DonauRsaIssueValue
  | DonauCsIssueValue;
interface DonauRsaIssueValue {
  cipher: "RSA";
}
interface DonauCsIssueValue {
  cipher: "CS";

  // CSR R0 value
  r_pub_0: CsRPublic;

  // CSR R1 value
  r_pub_1: CsRPublic;
}

1.12.4.2. Batch Issue

This is the effectiv issue receipts request. Depending on the amount of the donation a certain amount of blinded unique donation identifiers, or for short BUDIs, are required. Every BUDI will be signed by the corresponding requested donation unit, which is associated with a value. This API is used by the charity but the BUDIKeyPairs are coming from the donor.

To make the request idempotent, the hash of the hole request is recorded under the corresponding charity_id by the Donau.

POST /batch-issue/$CHARITY_ID

Send in a IssueReceiptsRequest and ask the Donau to sign all it’s contained BUDIs.

Request: IssueReceiptsRequest

Response:

200 OK:

The request was successful, and the response is a BlindedDonationReceiptSignatures.

403 Forbidden:

The charity signature is invalid. This response comes with a standard ErrorDetail response.

404 Not found:

At least one of the donation unit keys is not known to the Donau. Comes with a DonationUnitUnknownError. This suggests a bug in the donor as it should have used current donation unit keys from /keys.

409 Conflict:

The donation volume of the charity is not sufficient to issue donation receipts for all sent in blinded udis. The response is a IssueError object.

410 Gone:

The requested donation unit key is not yet or no longer valid. It either before the validity year, past the year or was revoked. The response is a DonationUnitExpiredMessage. Clients must evaluate the error code provided to understand which of the cases this is and handle it accordingly.

Details:

interface IssueReceiptsRequest {
  charity_sig: EddsaSignature;
  year: Integer;
  budikeypairs: BUDIKeyPairs[];
}
interface BUDIKeyPairs {
  h_donation_unit_pub: HashCode;
  blinded_udi: BUDI;
}
type BUDI = RSABUDI | CSBUDI ;
interface RSABUDI {
  cipher: "RSA";
  rsa_blinded_udi: string;          // Crockford Base32 encoded
}
// For donation unit signatures based on Blind Clause-Schnorr, the BUDI
// consists of the public nonce and two Curve25519 scalars which are two
// blinded challenges in the Blinded Clause-Schnorr signature scheme.
// See https://taler.net/papers/cs-thesis.pdf for details.
interface CSBUDI {
  cipher: "CS";
  cs_nonce: string;      // Crockford Base32 encoded
  cs_blinded_c0: string; // Crockford Base32 encoded
  cs_blinded_c1: string; // Crockford Base32 encoded
}
// Secret for blinding/unblinding.
// An RSA blinding secret, which is basically
// a 256-bit nonce, converted to Crockford Base32.
type BUDIBlindingKeyP = string;
interface BlindedDonationReceiptSignatures {
  blind_signed_receipt_signatures: BlindedDonationReceiptSignature[];
}
type BlindedDonationReceiptSignature =
  | RSABlindedDonationReceiptSignature
  | CSBlindedDonationReceiptSignature;
interface RSABlindedDonationReceiptSignature {
  cipher: "RSA";

  // (blinded) RSA signature
  blinded_rsa_signature: BlindedRsaSignature;
}
interface CSBlindedDonationReceiptSignature {
  type: "CS";

  // Signer chosen bit value, 0 or 1, used
  // in Clause Blind Schnorr to make the
  // ROS problem harder.
  b: Integer;

  // Blinded scalar calculated from c_b.
  s: Cs25519Scalar;
}
interface IssueError{
  max_per_year: Amount;
  current_year: Amount;
}
interface DonationUnitUnknownError{
  unknown_hash_pub_donation_unit: HashCode[];
  donau_pub: EddsaPublicKey;
  donau_sig: EddsaSignature;
}
interface DonationUnitExpiredMessage{
  h_donation_unit_pub: HashCode;
  donau_pub: EddsaPublicKey;
  donau_sig: EddsaSignature;
}

1.12.5. Donation statement

Inspired by the Taler exchange Deposit.

Donation statement operations are requested by a donor.

POST /batch-submit

Send in donation receipts for the current or one of the past fiscal years. The donor will reveive the signed total back, which is called the donation statement.

Request: SubmitDonationReceiptsRequest

Response:

201 Created:

The request was successful, and the created DonationStatement is stored.

403 Forbidden:

One of the signatures is invalid. This response comes with a standard ErrorDetail response.

404 Not found:

At least one of the donation unit keys is not known to the Donau. Comes with a DonationUnitUnknownError.

Details:

interface SubmitDonationReceiptsRequest{
  // hashed taxpayer ID plus salt
  h_donor_tax_id: HashCode;
  // All donation receipts must be for this year.
  donation_year: Integer;
  // Receipts should be sorted by amount.
  donation_receipts: DonationReceipt[];
}
interface DonationReceipt{
  h_donation_unit_pub: HashCode;
  nonce: string;
  donation_unit_sig: DonationReceiptSignature
}
type DonationReceiptSignature = RSADonationReceiptSignature | CSDonationReceiptSignature ;
interface RSADonationReceiptSignature {
  cipher: "RSA";

  // RSA signature
  rsa_signature: RsaSignature;
}
interface CSDonationReceiptSignature {
  type: "CS";

  // R value component of the signature.
  cs_signature_r: Cs25519Point;

  // s value component of the signature.
  cs_signature_s: Cs25519Scalar;
}
GET /donation-statement/$YEAR/$HASH_DONOR_ID

Get the donation statement for a specific year and donor.

Request:

Response:

200 OK:

The request was successful, and the response is a DonationStatementResponse.

403 Forbidden:

One of the signatures is invalid. This response comes with a standard ErrorDetail response.

404 Not found:

Either the donor or the year or the corresponding donation statement was not found. This response comes with a standard ErrorDetail response.

Details:

interface DonationStatementResponse {
  total: Amount;
  // signature over h_donor_tax_id, total, donation_year
  donation_statement_sig: EddsaSignature;
  // the corresponding public key to the signature
  donau_pub: EddsaPublicKey;
}

1.12.6. Charity administration and status information

The administration requests require an authorized bearer token to be set in the HTTP “Authorization” Header. This token can be set by a proxy validating authentication/authorization (using e.g. LDAP). The GET status requests require an authorized bearer token as well.

GET /charities

GET all charities. Only allowed if the request comes with the administration bearer token.

return all charities

Request:

Reponse:

200 OK:

The request was successful, and the response is a Charities.

Details:

interface Charities{
  charities: CharitySummary[];
}
interface CharitySummary{
  charity_id: Integer;
  name: string;
  max_per_year: Amount;
  receipts_to_date: Amount;
}
GET /charities/$CHARITY_ID

GET a specific charity. Only allowed if the request comes with the charity or administration bearer token.

Request information about a charity.

Request:

Response:

200 OK:

The Donau responds with a Charity object

404 Not found:

The charity id does not belong to a charity known to the Donau.

interface Charity {
  charity_pub: EddsaPublicKey;
  name: string;
  url: string;
  max_per_year: Amount;
  receipts_to_date: Amount;
  current_year: Integer;
}
POST /charity

Add a charity. Only allowed if the request comes with the administrator bearer token.

Request: CharityRequest

Response:

Details:

201 Created:

The request was successful, and the response is a CharityResponse.

403 Forbidden:

The request did not contain an accepted administrator bearer token in it’s header.

interface CharityRequest{
  charity_pub: EddsaPublicKey;
  name: string;
  url: string;
  max_per_year: Amount;
  receipts_to_date: Amount;
  current_year: Integer;
}
interface CharityResponse{
  id: Integer;
}
PATCH /charities/{id}

Modify a charity. Only allowed if the request comes with the administrator bearer token.

Request: CharityRequest

Response:

200 OK:

The request was successful.

403 Forbidden:

The request did not contain an accepted administrator bearer token in it’s header.

DELETE /charities/{id}

Delete (or deactivate) a charity. Only allowed if the request comes with the administrator bearer token.

Request:

Response:

204 No content:

The request was successful.

403 Forbidden:

The request did not contain an accepted administrator bearer token in it’s header.