The service exposes an HTTP Restful API with the following endpoints.
The group of calls below is intended for the admin running the service itself.
Superadmin related
Example
curl -X POST -H "Authorization: Bearer <superadmin-key>" https://server/v1/admin/accounts
{
"cspUrlPrefix": "my-csp-url-prefix",
"cspPubKey": "hexBytes",
"name": "My integrator account",
"email": "admin@account.net"
}
{
"id": "1234567890",
"apiKey": "ksjdhfksjdh..." // The integrator (secret) key to use the API
}
{
"error": "Message goes here"
}
Example
curl -X PUT -H "Authorization: Bearer <superadmin-key>" https://server/v1/admin/accounts/<id>
{
"cspUrlPrefix": "my-csp-url-prefix",
"cspPubKey": "hexBytes",
"name": "My integrator account",
}
{
"id": "1234567890"
}
{
"error": "Message goes here"
}
Example
curl -X PATCH -H "Authorization: Bearer <superadmin-key>" https://server/v1/admin/accounts/<id>/key
{
"id": "1234567890",
"apiKey": "priv_abcdefghijklmnoprstuvwxyz"
}
{
"error": "Message goes here"
}
Example
curl -X DELETE -H "Authorization: Bearer <superadmin-key>" https://server/v1/admin/accounts/<id>
// empty response
{
"error": "Message goes here"
}
Integrator related
The following endpoints are authenticated by using the integrator secret key. They allow integrators to manage the organizations related to their customers.
Example
This request submits a transaction to the [voting blockchain](../architecture/services/vochain.md) which can take some time (~15 seconds) to be accepted and mined. Therefore, the return values of this method should not be considered valid until the Transaction Status method is called, using the `txHash` value to confirm that the desired transaction has been mined. Only then is it safe to query for the organization you have created.curl -X POST -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/account/organizations
{
"name": "Organization name",
"description": "my-description",
"header": "https://my/header.jpeg",
"avatar": "https://my/avatar.png"
}
{
"organizationId": "0x1234...",
"apiToken": "pub_qwertyui...", // API token for public voting endpoints
"txHash": "0x1234...",
}
{
"error": "Message goes here"
}
Example
curl -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/account/organizations/<organizationId>
{
"apiToken": "qoiuwhgoiauhsdaiouh", // the public API token
"name": "Organization name",
"description": "",
"header": "https://my/header.jpeg",
"avatar": "https://my/avatar.png"
}
{
"error": "Message goes here"
}
Example
curl -X DELETE -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/account/organizations/<organizationId>
// empty response
{
"error": "Message goes here"
}
Example
curl -X PATCH -H "Authorization: Bearer <integrator-key>" https://server/v1/account/organizations/<id>/key
{
"apiToken": "zxcvbnm"
}
{
"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.
Example
This request submits a transaction to the [voting blockchain](../architecture/services/vochain.md) which can take some time (~15 seconds) to be accepted and mined. Therefore, the return values of this method should not be considered valid until the Transaction Status method is called, using the `txHash` value to confirm that the desired transaction has been mined. Only then is it safe to query for the organization you have updated.curl -X PUT -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/organizations/<organizationId>/metadata
{
"name": "Organization name",
"description": "my-description",
"header": "https://my/header.jpeg",
"avatar": "https://my/avatar.png"
}
{
"organizationId": "0x1234...",
"contentUri": "ipfs://1234...",
"txHash": "0x1234...",
}
{
"error": "Message goes here"
}
Example
curl -X GET -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/transactions/<transaction-hash>
{
"mined": true // true | false
}
{
"error": "Message goes here"
}
Generates a Merkle Tree with the given current census keys and generates a voting process with the given metadata.
Example
This request submits a transaction to the [voting blockchain](../architecture/services/vochain.md) which can take some time (~15 seconds) to be accepted and mined. Therefore, the return values of this method should not be considered valid until the Transaction Status method is called, using the `txHash` value to confirm that the desired transaction has been mined. Only then is it safe to query for the election you have created.curl -X POST -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/organizations/<organizationId>/elections/signed
curl -X POST -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/organizations/<organizationId>/elections/blind
{
"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": false, // Metadata access restricted to only census members
"hiddenResults": true, // Encrypt results until the election ends
"census": "<censusId>" // Optional for CSP processes
}
{
"electionId": "0x1234...",
"txHash": "0x1234...",
}
{
"error": "Message goes here"
}
Generates a Merkle Tree with the given current census keys and generates a voting process with the given metadata.
Example
This request submits a transaction to the [voting blockchain](../architecture/services/vochain.md) which can take some time (~15 seconds) to be accepted and mined. Therefore, the desired results of this method should not be considered valid until the Transaction Status method is called, using the `txHash` value to confirm that the desired transaction has been mined. Only then is it safe to query for the election you have updated.curl -X PUT -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/elections/<electionId>/status
{
"status": "READY" // READY, ENDED, CANCELED, PAUSED
}
{
"txHash": "0x1234..."
}
{
"error": "Message goes here"
}
Allows unrestricted listing, paging and filtering for the integrator backend to display all info to organization admins.
Example
curl -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/organizations/<organizationId>/elections
curl -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/organizations/<organizationId>/elections/active
curl -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/organizations/<organizationId>/elections/ended
curl -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/organizations/<organizationId>/elections/upcoming
[
{
"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",
}, {...}
]
{
"error": "Message goes here"
}
Allows unrestricted access for the integrator backend to display all info to organization admins. Confidential elections do not require any additional step, just the integrator API key.
Example
curl -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/elections/<electionId>
{
"type": "blind-confidential-hidden-results",
"organizationId": "20323909c3e0965d1489893db1512b32b55707ea",
"title": "test election",
"description": "description test 1",
"header": "https://source.unsplash.com/random/800x600",
"questions": [
{
"title": "test q1",
"description": "",
"choices": [
"Yes",
"No"
]
},
{
"title": "test q2",
"description": "",
"choices": [
"Yes",
"No"
]
},
{
"title": "test q3",
"description": "",
"choices": [
"Yes",
"No"
]
}
],
"status": "Results",
"streamUri": "uri",
"voteCount": 1,
"results": [
{
"title": [
"Yes",
"No"
],
"value": [
"1",
"0"
]
},
{
"title": [
"Yes",
"No"
],
"value": [
"1",
"0"
]
},
{
"title": [
"Yes",
"No"
],
"value": [
"1",
"0"
]
}
],
"organizationId": "20323909c3e0965d1489893db1512b32b55707ea",
"electionId": "47f2c1f1164a27db4f5e7b825f8ec064c44da88a83ff72b90e5755fff8bfb53b",
"aggregation": "discrete-counting",
"display": "multiple-question"
}
{
"electionId": "0x1234..."
}
{
"error": "Message goes here"
}
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
curl -X POST -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/censuses
{
"name": "2021 members"
}
{
"censusId": "123456789..."
}
{
"error": "Message goes here"
}
Creates N census tokens for voters to register their public key in the future.
Example
curl -X POST -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/censuses/<censusId>/tokens/flat
{
"size": 450
}
{
"censusId": "123456789...",
"size": 700, // new size
"tokens": [
"jashdlkfjahs", "uyroeituwyert", "e7rg9e87rn9", ... // x 450
]
}
{
"error": "Message goes here"
}
Creates weighted census tokens so that voters with the token can register their public key to the appropriate census weight.
Example
curl -X POST -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/censuses/<censusId>/tokens/weighted
{
"weights": [
40, 70, 200
]
}
{
"censusId": "123456789...",
"size": 700, // new size
"tokens": [
{ "token": "jashdlkfjahs", "weight": 40 },
{ "token": "uyroeituwyert", "weight": 70 },
{ "token": "e7rg9e87rn9", "weight": 200 }
]
}
{
"error": "Message goes here"
}
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
curl -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/censuses/<censusId>/tokens/<tokenId>
{
"publicKey": "", // no public key yet
"weight": 20
}
{
"error": "Message goes here"
}
Removes the given token or key from the given census. The next time it is used, the new key will be in effect.
Example
curl -X DELETE -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/censuses/<censusId>/tokens/<tokenId>
curl -X DELETE -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/censuses/<censusId>/keys/<publicKey>
// empty response
{
"error": "Message goes here"
}
Import a group of public keys to an existing census. All voters have the same weight (1).
Example
curl -X POST -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/censuses/<censusId>/import/flat
{
"publicKeys": [
"0x0312345678...",
"0x0223456789...",
...
]
}
{
"censusId": "123456789...",
"size": 300
}
{
"error": "Message goes here"
}
Import a group of public keys to an existing census, using their respective weight.
Example
curl -X POST -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/censuses/<censusId>/import/weighted
{
"entries": [
{ "publicKey": "0x0312345678...", "weight": 100 },
{ "publicKey": "0x02234567890...", "weight": 150 },
...
]
}
{
"censusId": "123456789...",
"size": 300
}
{
"error": "Message goes here"
}
Example
curl -X PUT -H "Authorization: Bearer <integrator-key>" https://server/v1/priv/elections/<electionId>/status
{
"status": "PAUSED" // READY, PAUSED, ENDED, CANCELED
}
{
"censusId": "123456789..."
}
{
"error": "Message goes here"
}
(token API authenticated, voter apps call it directly)
Example
curl -H "Authorization: Bearer <organization-api-token>" https://server/v1/pub/organizations/<organizationId>
{
"name": "Organization name",
"description": "",
"header": "https://my/header.jpeg",
"avatar": "https://my/avatar.png"
}
{
"error": "Message goes here"
}
Example
curl -H "Authorization: Bearer <manager-key>" https://server/v1/pub/organizations/<organizationId>/elections
curl -H "Authorization: Bearer <manager-key>" https://server/v1/pub/organizations/<organizationId>/elections/active
curl -H "Authorization: Bearer <manager-key>" https://server/v1/pub/organizations/<organizationId>/elections/ended
curl -H "Authorization: Bearer <manager-key>" https://server/v1/pub/organizations/<organizationId>/elections/upcoming
[
{
"id": "0x12345678...",
"title": "Important election",
"avatar": "https://my/avatar.png",
"startDate": "2021-12-25T11:20:53.769Z",
"endDate": "2021-10-30T12:00:00.000Z",
"confidential": false,
"hiddenResults": true
}, {...}
]
{
"error": "Message goes here"
}
Example
curl -H "Authorization: Bearer <organization-api-token>" https://server/v1/pub/elections/<electionId>
{
"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" } ]
]
}
{
"error": "Message goes here"
}
Provides the details of a confidential voting process if the user holds a wallet that belongs to its census.
If {electionId}
has been signed by the CSP, then it gets the Vochain parameters, decrypts the metadata and returns it to the caller.
URL Params:
- election-id
- signed-pid:
sign({electionId}, voterPrivK)
- csp-signature:
sign({electionId}, cspPrivK)
Example
curl -H "Authorization: Bearer <organization-api-token>" https://server/v1/pub/elections/<election-id>/auth/<csp-shared-key>
{
"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" } ]
]
}
{
"error": "Message goes here"
}
People voting on a signed process will need to package a vote envelope using the result of this call.
Note: This call does not apply to deployments where a custom CSP validation is being used.
Example
curl -H "Authorization: Bearer <organization-api-token>" https://server/v1/pub/elections/<electionId>/proof
{
"signature": "0x12345678..." // signing a well-known payload using the wallet
}
{
"proof": "..."
}
{
"error": "Message goes here"
}
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
curl -X POST -H "Authorization: Bearer <organization-api-token>" https://server/v1/pub/elections/<electionId>/vote
{
"vote": "<base64-signed-vote-transaction>" // see dvote-js
}
{
"nullifier": "0x12345678..."
}
{
"error": "Message goes here"
}
Voters can check the status of their vote here, and eventually check the explorer link, for independent confirmation.
Example
curl -H "Authorization: Bearer <organization-api-token>" https://server/v1/pub/nullifiers/<nullifier>
{
"electionId": "0x12345678...",
"registered": true,
"explorerUrl": "https://vaas.explorer.vote/nullifiers/0x12345678"
}
{
"error": "Message goes here"
}
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
curl -X POST -H "Authorization: Bearer <organization-api-token>" https://server/v1/pub/censuses/<censusId>/token
{
"censusToken": "jashdlkfjahs",
"publicKey": "0x03abcdef0123456789..."
}
// empty response
{
"error": "Message goes here"
}
Generic authentication
The CSP issues a per-process signature whenever the 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 electionID
to prove that he/she has a private key within the election census. If everything is correct, the CSP returns sign({electionId}, cspPrivK)
.
- election-id
- signed-pid:
sign({electionId}, voterPrivK)
Example
curl -H "Authorization: Bearer <organization-api-token>" https://server/v1/auth/elections/<election-id>/sharedkey
{
"authData": ["<signed-pid>"]
}
{
"sharedkey": "0x1234567890abcde..."
}
{
"error": "Message goes here"
}
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
- signed-pid:
sign({electionId}, voterPrivK)
Example
curl -X POST -H "Authorization: Bearer <organization-api-token>" https://server/v1/auth/elections/<election-id>/ecdsa/auth
curl -X POST -H "Authorization: Bearer <organization-api-token>" https://server/v1/auth/elections/<election-id>/blind/auth
{
"authData": ["<signed-pid>"]
}
{
"tokenR": "0x1234567890abcde..."
}
{
"error": "Message goes here"
}
The user generates an ephemeral wallet and the received tokenR to generate a (plain or blinded) payload. This payload is sent to the backend, which will check the correctness and reply with a signature of the payload.
The voter then may unblind the response (if applicable) and use it as their vote signature.
Example
curl -X POST -H "Authorization: Bearer <organization-api-token>" https://server/v1/auth/elections/<electionId>/ecdsa/sign
curl -X POST -H "Authorization: Bearer <organization-api-token>" https://server/v1/auth/elections/<electionId>/blind/sign
{
"payload": "0xabcdef...", // hash({electionId, address}) or blind(hash({electionId, address}))
"tokenR": "0x1234567890abcde..."
}
{
"signature": "0x1234567890abcde..." // plain or blind signature
}
{
"error": "Message goes here"
}
For transparency, external observers can request the exhaustive list of public keys that made a blind signature request.
Example
curl -H "Authorization: Bearer <organization-api-token>" https://server/v1/pub/elections/<electionId>/blind/authorized
{
"publicKeys": [
"0x12345678...",
"0x23456789...",
...
]
}
{
"error": "Message goes here"
}
Custom authentication API
This endpoint is conceptually the same as the one from above. The only difference lies on the custom logic that decides whether a voter is eligible or not.
The CSP issues a per-process signature whenever the wallet belongs to the process's census. The signature can be used to retrieve confidential information, restricted to only census members.
If the evidence provided is correct, the CSP returns sign({electionId}, cspPrivK)
.
- election-id
- signed-pid:
sign({electionId}, voterPrivK)
Example
curl -X POST -H "Authorization: Bearer <organization-api-token>" https://server/v1/auth/elections/<election-id>/sharedkey
{
"authData": ["param1", "param2", ...]
}
#### HTTP 200
```json
{
"sharedkey": "0x1234567890abcde..."
}
{
"error": "Message goes here"
}
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.