# Integration API Specification

The service exposes an HTTP Restful API with the following endpoints.

# Internal API

The group of calls below is intended for the admin running the service itself.

Superadmin related


# Create an integrator account

Example

# Request

curl -X POST -H "Bearer: <superadmin-key>" https://server/v1/admin/accounts

# Request body

{
	"cspUrlPrefix": "my-csp-url-prefix",
	"cspPubKey": "hexBytes",
	"name": "My integrator account",
}

# HTTP 200

{
	"id": "1234567890",
	"apiKey": "ksjdhfksjdh..."   // The integrator (secret) key to use the API
}

# HTTP 400

{
    "error": "Message goes here"
}

# Update an integrator account

Example

# Request

curl -X PUT -H "Bearer: <superadmin-key>" https://server/v1/admin/accounts/<id>

# Request body

{
    "name": "My integrator account",
    "plan": "billing-plan-key"
}

# HTTP 200

{
    "id": "1234567890"
}

# HTTP 400

{
    "error": "Message goes here"
}

# Reset an integrator API key

Example

# Request

curl -X PATCH -H "Bearer: <superadmin-key>" https://server/v1/admin/accounts/<id>/key

# HTTP 200

{
    "id": "1234567890",
    "apiKey": "zxcvbnm"
}

# HTTP 400

{
    "error": "Message goes here"
}

# Get an integrator account

Example

# Request

curl -H "Bearer: <superadmin-key>" https://server/v1/admin/accounts/<id>

# HTTP 200

{
    "name": "My integrator account",
    "plan": "billing-plan-key"
}

# HTTP 400

{
    "error": "Message goes here"
}

# Delete an integrator account

Example

# Request

curl -X DELETE -H "Bearer: <superadmin-key>" https://server/v1/admin/accounts/<id>

# HTTP 200

// empty response

# HTTP 400

{
    "error": "Message goes here"
}

# Integrator API (Private)

Integrator related


The following endpoints are authenticated by using the integrator secret key. They allow integrators to manage the organizations related to their customers.

# Create an organization

Example

# Request

curl -X POST -H "Bearer: <integrator-key>" https://server/v1/priv/account/organizations

# Request body

{
    "name": "Organization name",
    "description": "",
    "header": "https://my/header.jpeg",
    "avatar": "https://my/avatar.png"
}

# HTTP 200

{
    "organizationId": "0x1234...",
    "apiToken": "qwertyui...",   // API token for public voting endpoints
    "apiKey": "asdfghjk..."      // Secret API key to manage the organization
}

# HTTP 400

{
    "error": "Message goes here"
}

# Get an organization

Example

# Request

curl -H "Bearer: <integrator-key>" https://server/v1/priv/account/organizations/<organizationId>

# HTTP 200

{
    "apiToken": "qoiuwhgoiauhsdaiouh",   // the public API token
    "name": "Organization name",
    "description": "",
    "header": "https://my/header.jpeg",
    "avatar": "https://my/avatar.png"
}

# HTTP 400

{
    "error": "Message goes here"
}

# Remove an organization

Example

# Request

curl -X DELETE -H "Bearer: <integrator-key>" https://server/v1/priv/account/organizations/<organizationId>

# HTTP 200

// empty response

# HTTP 400

{
    "error": "Message goes here"
}

# Reset the public API key of an organization

Example

# Request

curl -X PATCH -H "Bearer: <integrator-key>" https://server/v1/account/organizations/<id>/key

# HTTP 200

{
    "id": "1234567890",
    "apiKey": "zxcvbnm"
}

# HTTP 400

{
    "error": "Message goes here"
}

Organization related


These methods are also intended for integrators, but they are expected to do the duties of an organization managing a proposal.

# Set Organization metadata

Example

# Request

curl -X PUT -H "Bearer: <integrator-key>" https://server/v1/priv/organizations/<organizationId>/metadata

# Request body

{
    "name": "Organization name",
    "description": "",
    "header": "https://my/header.jpeg",
    "avatar": "https://my/avatar.png"
}

# HTTP 200

{
    "organizationId": "0x1234..."
}

# HTTP 400

{
    "error": "Message goes here"
}

# Create a process

Generates a Merkle Tree with the given current census keys and generates a voting process with the given metadata.

Example

# Request

curl -X POST -H "Bearer: <integrator-key>" https://server/v1/priv/organizations/<organizationId>/processes/signed
curl -X POST -H "Bearer: <integrator-key>" https://server/v1/priv/organizations/<organizationId>/processes/blind

# Request body

{
    "title": "Important election",
    "description": "Description here",
    "header": "https://my/header.jpeg",
    "streamUri": "https://youtu.be/1234",
    "startDate": "2021-10-25T11:20:53.769Z", // can be empty
    "endDate": "2021-10-30T12:00:00.000Z",
    "questions": [
        {
            "title": "Question 1",
            "description": "(optional)",
            "choices": ["Yes", "No", "Maybe"]  // simplified version of title/value
        }, {...}
    ],
    "confidential": true,  // Metadata access restricted to only census members
    "hiddenResults": true, // Encrypt results until the process ends
    "census": "<censusId>"
}

# HTTP 200

{
    "processId": "0x1234..."
}

# HTTP 400

{
    "error": "Message goes here"
}

# List processes (filtered)

Allows unrestricted listing, paging and filtering for the integrator backend to display all info to organization admin's.

Example

# Request

curl -H "Bearer: <integrator-key>" https://server/v1/priv/organizations/<organizationId>/processes/signed
curl -H "Bearer: <integrator-key>" https://server/v1/priv/organizations/<organizationId>/processes/blind

curl -H "Bearer: <integrator-key>" https://server/v1/priv/organizations/<organizationId>/processes/active
curl -H "Bearer: <integrator-key>" https://server/v1/priv/organizations/<organizationId>/processes/ended
curl -H "Bearer: <integrator-key>" https://server/v1/priv/organizations/<organizationId>/processes/upcoming

# HTTP 200

[
    {
        "title": "Important election",
        "description": "",
        "header": "https://my/header.jpeg",
        "status": "READY",
        "startDate": "2021-10-25T11:20:53.769Z", // can be empty
        "endDate": "2021-10-30T12:00:00.000Z",
    }, {...}
]

# HTTP 400

{
    "error": "Message goes here"
}

# Get a process

Allows unrestricted access for the integrator backend to display all info to organization admin's. Confidential processes do not require any additional step, just the integrator API key.

Example

# Request

curl -H "Bearer: <integrator-key>" https://server/v1/priv/processes/<processId>

# Request body

{
    "type": "signed", // blind, ...
    "title": "Important election",
    "description": "Description goes here",
    "header": "https://my/header.jpeg",
    "streamUri": "https://youtu.be/1234",
    "questions": [
        {
            "title": "Question 1",
            "description": "(optional)",
            "choices": ["Yes", "No", "Maybe"]
        }, {...}
    ],
    "confidential": true,  // Metadata access restricted to only census members
    "hiddenResults": true, // Encrypt results until the process ends
    "census": "<censusId>",
    "status": "PAUSED"
}

# HTTP 200

{
    "processId": "0x1234..."
}

# HTTP 400

{
    "error": "Message goes here"
}

# Create a census

A census where public keys or token slots (that will eventually contain a public key) are stored. A census can start with 0 items, and public keys can be imported later on.

If census tokens are allocated, users will need to generate a wallet on the frontend and register the public key by themselves. This prevents both the API and the integrator from gaining access to the private key.

Example

# Request

curl -X POST -H "Bearer: <integrator-key>" https://server/v1/priv/censuses

# Request body

{
    "name": "2021 members"
}

# HTTP 200

{
    "censusId": "123456789..."
}

# HTTP 400

{
    "error": "Message goes here"
}

# Add N census tokens (once)

Creates N census tokens for voters to register their public key in the future.

Example

# Request

curl -X POST -H "Bearer: <integrator-key>" https://server/v1/priv/censuses/<censusId>/tokens/flat

# Request body

{
    "size": 450
}

# HTTP 200

{
    "censusId": "123456789...",
    "size": 700,  // new size
    "tokens": [
        "jashdlkfjahs", "uyroeituwyert", "e7rg9e87rn9", ... // x 450
    ]
}

# HTTP 400

{
    "error": "Message goes here"
}

# Add weighted census tokens (once)

Creates weighted census tokens so that voters with the token can register their public key to the appropriate census weight.

Example

# Request

curl -X POST -H "Bearer: <integrator-key>" https://server/v1/priv/censuses/<censusId>/tokens/weighted

# Request body

{
    "weights": [
        40, 70, 200
    ]
}

# HTTP 200

{
    "censusId": "123456789...",
    "size": 700,  // new size
    "tokens": [
        { "token": "jashdlkfjahs", weight: 40 },
        { "token": "uyroeituwyert", weight: 70 },
        { "token": "e7rg9e87rn9", weight: 200 }
    ]
}

# HTTP 400

{
    "error": "Message goes here"
}

# Get census token (see a registered public key)

Allows integrators to check the status of a census token, given to a user.

If the token has already been redeemed, the public key will be used as part of the census normally.

Example

# Request

curl -H "Bearer: <integrator-key>" https://server/v1/priv/censuses/<censusId>/tokens/<tokenId>

# HTTP 200

{
    "publicKey": "",   // no public key yet
    "weight": 20
}

# HTTP 400

{
    "error": "Message goes here"
}

# Remove a census token or public key from a census

Removes the given token or key from the given census. The next time it is used, the new key will be in effect.

Example

# Request

curl -X DELETE -H "Bearer: <integrator-key>" https://server/v1/priv/censuses/<censusId>/tokens/<tokenId>
curl -X DELETE -H "Bearer: <integrator-key>" https://server/v1/priv/censuses/<censusId>/keys/<publicKey>

# HTTP 200

// empty response

# HTTP 400

{
    "error": "Message goes here"
}

# Import public keys into a census

Import a group of public keys to an existing census. All voters have the same weight (1).

Example

# Request

curl -X POST -H "Bearer: <integrator-key>" https://server/v1/priv/censuses/<censusId>/import/flat

# Request body

{
    "publicKeys": [
        "0x0312345678...",
        "0x0223456789...",
        ...
    ]
}

# HTTP 200

{
    "censusId": "123456789...",
    "size": 300
}

# HTTP 400

{
    "error": "Message goes here"
}

# Import weighted public keys into a census

Import a group of public keys to an existing census, using their respective weight.

Example

# Request

curl -X POST -H "Bearer: <integrator-key>" https://server/v1/priv/censuses/<censusId>/import/weighted

# Request body

{
    "entries": [
        { "publicKey": "0x0312345678...", "weight": 100 },
        { "publicKey": "0x02234567890...", "weight": 150 },
        ...
    ]
}

# HTTP 200

{
    "censusId": "123456789...",
    "size": 300
}

# HTTP 400

{
    "error": "Message goes here"
}

# End/start/pause/cancel a process

Example

# Request

curl -X PUT -H "Bearer: <integrator-key>" https://server/v1/priv/processes/<processId>/status

# Request body

{
    "status": "PAUSED" // READY, PAUSED, ENDED, CANCELED
}

# HTTP 200

{
    "censusId": "123456789..."
}

# HTTP 400

{
    "error": "Message goes here"
}

# Public API

(token API authenticated, voter apps call it directly)

# Get Organization data

Example

# Request

curl -H "Bearer: <organization-api-token>" https://server/v1/pub/organizations/<organizationId>

# HTTP 200

{
    "name": "Organization name",
    "description": "",
    "header": "https://my/header.jpeg",
    "avatar": "https://my/avatar.png"
}

# HTTP 400

{
    "error": "Message goes here"
}

# Get process list (per organization)

Example

# Request

curl -H "Bearer: <manager-key>" https://server/v1/pub/organizations/<organizationId>/processes/active
curl -H "Bearer: <manager-key>" https://server/v1/pub/organizations/<organizationId>/processes/ended
curl -H "Bearer: <manager-key>" https://server/v1/pub/organizations/<organizationId>/processes/upcoming

# HTTP 200

[
    {
        "title": "Important election",
        "description": "",
        "header": "https://my/header.jpeg",
        "status": "READY",
        "startDate": "2021-10-25T11:20:53.769Z", // can be empty
        "endDate": "2021-10-30T12:00:00.000Z",
    }, {...}
]

# HTTP 400

{
    "error": "Message goes here"
}

# Get process info – non-confidential

Example

# Request

curl -H "Bearer: <organization-api-token>" https://server/v1/pub/processes/<processId>

# HTTP 200

{
    "type": "signed-plain", // blind-plain, ...
    "title": "Important election",
    "description": "Description goes here",
    "header": "https://my/header.jpeg",
    "streamUri": "https://youtu.be/1234",
    "questions": [
        {
            "title": "Question 1",
            "description": "(optional)",
            "choices": ["Yes", "No", "Maybe"]
        }, {...}
    ],
    "status": "READY",
    "voteCount": 1234,
    "results": [   // Empty array when no results []
        [ { "title": "Yes", "value": 1234, "title": "No", "value": 2345 } ],
        [ { "title": "Yes", "value": 22, "title": "No", "value": 33 } ]
    ]
}

# HTTP 400

{
    "error": "Message goes here"
}

# Get process info – confidential

Provides the details of a confidential voting process if the user holds a wallet that belongs to its census.

If {processId, address} has been signed by the CSP, then it gets the Vochain parameters, decrypts the metadata and returns it to the caller.

URL Params:

Example

# Request

curl -H "Bearer: <entity-api-token>" https://server/v1/pub/processes/<process-id>/auth/<pid-signed>/<csp-signature>

# HTTP 200

{
    "type": "signed-plain", // blind-plain, ...
    "title": "Important election",
    "description": "Description goes here",
    "header": "https://my/header.jpeg",
    "streamUri": "https://youtu.be/1234",
    "questions": [
        {
            "title": "Question 1",
            "description": "(optional)",
            "choices": ["Yes", "No", "Maybe"]
        }, {...}
    ],
    "status": "READY",
    "voteCount": 1234,
    "results": [   // Empty array when no results []
        [ { "title": "Yes", "value": 1234, "title": "No", "value": 2345 } ],
        [ { "title": "Yes", "value": 22, "title": "No", "value": 33 } ]
    ]
}

# HTTP 400

{
    "error": "Message goes here"
}

# Requesting a census proof

People voting on a signed process will need to package a vote envelope using the result of this call.

Example

# Request

curl -H "Bearer: <organization-api-token>" https://server/v1/pub/processes/<processId>/proof

# Request body

{
    "signature": "0x12345678..." // signing a well-known payload using the wallet
}

# HTTP 200

{
    "proof": "..."
}

# HTTP 400

{
    "error": "Message goes here"
}

# Submitting a vote (signed or blind)

Voters using the tiny JS SDK will get a base64 bundle including the vote and the census proof. This payload is submitted as a base64 string.

Example

# Request

curl -X POST -H "Bearer: <organization-api-token>" https://server/v1/pub/processes/<processId>/vote

# Request body

{
    "vote": "<base64-signed-vote-transaction>" // see dvote-js
}

# HTTP 200

{
    "nullifier": "0x12345678..."
}

# HTTP 400

{
    "error": "Message goes here"
}

# Getting a ballot (nullifier)

Voters can check the status of their vote here, and eventually check the explorer link, for independent confirmation.

Example

# Request

curl -H "Bearer: <organization-api-token>" https://server/v1/pub/nullifiers/<nullifier>

# HTTP 200

{
    "processId": "0x12345678...",
    "registered": true,
    "explorerUrl": "https://vaas.explorer.vote/nullifiers/0x12345678"
}

# HTTP 400

{
    "error": "Message goes here"
}

# Registering a voter's public key

This process needs to be done by the integrator's frontend, once.

As soon as a user runs an updated UI version, a new private key should be generated, and the public key should be registered, so that new votes can use this key.

If the wallet is lost, the integrator will need to remove the pubKey from the census and create a new census token when the new wallet is available.

Example

# Request

curl -X POST -H "Bearer: <organization-api-token>" https://server/v1/pub/censuses/<censusId>/token

# Request body

{
    "censusToken": "jashdlkfjahs",
    "publicKey": "0x03abcdef0123456789..."
}

# HTTP 200

// empty response

# HTTP 400

{
    "error": "Message goes here"
}

# Authentication API

Generic authentication


# Get a plain signature

The CSP issues a signature to prove that a wallet belongs to the process's census. The signature can be used to retrieve confidential information, restricted to only census members.

The voter signs the processID to prove that he/she has a private key within the election census. If everything is correct, the CSP returns sign({ processId, address }, cspPrivK).

  • process-id
  • pid-signed: sign(processId, voterPrivK)
Example

# Request

curl -H "Bearer: <entity-api-token>" https://server/v1/auth/processes/<process-id>/plain/auth/<pid-signed>

# HTTP 200

{
    "signature": "0x1234567890abcde..."
}

# HTTP 400

{
    "error": "Message goes here"
}

# Get a token for requesting a blind signature

The blind signature process involves a two step interaction.

In the first interaction, the voter proves to have a private key within the election census. If everything is correct, the backend replies with the tokenR, which the voter needs to use on the second step.

  • process-id
  • pid-signed: sign(processId, voterPrivK)
Example

# Request

curl -H "Bearer: <entity-api-token>" https://server/v1/auth/processes/<process-id>/blind/auth/<signed-pid>

# HTTP 200

{
    "tokenR": "0x1234567890abcde..."
}

# HTTP 400

{
    "error": "Message goes here"
}

# Request the blind signature for an ephemeral wallet

The user generates an ephemeral wallet and the received tokenR to generate a blinded payload. This payload is sent to the backend, which will check the correctness and reply with a signature of the blinded payload.

The voter then unblinds the response and uses it as their vote signature.

Example

# Request

curl -X POST -H "Bearer: <organization-api-token>" https://server/v1/auth/processes/<processId>/blind/sign

# Request body

{
    "blindedPayload": "0xabcdef...",   // blind(hash({processId, address}))
    "tokenR": "0x1234567890abcde..."
}

# HTTP 200

{
    "blindSignature": "0x1234567890abcde..."
}

# HTTP 400

{
    "error": "Message goes here"
}

# Get the public keys that have requested a blind signature on a process

For transparency, external observers can request the exhaustive list of public keys that made a blind signature request.

Example

# Request

curl -H "Bearer: <organization-api-token>" https://server/v1/pub/processes/<processId>/blind/authorized

# HTTP 200

{
    "publicKeys": [
        "0x12345678...",
        "0x23456789...",
        ...
    ]
}

# HTTP 400

{
    "error": "Message goes here"
}

Custom authentication API


# Get a token for requesting a blind signature - custom

This endpoint is conceptually the same as the one from above. The only difference lies on the custom logic that decides whether a tokenR is generated or not.

The blind signature process involves a two step interaction.

In the first interaction, the voter proves their eligibility. If everything is correct, the backend replies with the tokenR, which the voter needs to use on the second step.

Example

# Request

curl -X POST -H "Bearer: <organization-api-token>" https://server/v1/auth/custom/processes/<processId>/blind/auth

# Request body

{
    "voterId": "0x1234...",
    "caSignature": "..."
}

# HTTP 200

{
    "tokenR": "0x1234567890abcde..."
}

# HTTP 400

{
    "error": "Message goes here"
}
Last Updated: 12/1/2021, 5:34:07 PM