github.com/decred/politeia@v1.4.0/politeiawww/api/www/v1/api.md (about) 1 # politeiawww API Specification 2 3 This document describes the REST API provided by a `politeiawww` server. The 4 `politeiawww` server is the web server backend and it interacts with a JSON 5 REST API. This document also describes websockets for server side 6 notifications. It does not render HTML. 7 8 # v1 9 10 **Methods** 11 12 - [`Version`](#version) 13 - [`Policy`](#policy) 14 - [`New user`](#new-user) 15 - [`Verify user`](#verify-user) 16 - [`Resend verification`](#resend-verification) 17 - [`Me`](#me) 18 - [`Login`](#login) 19 - [`Logout`](#logout) 20 - [`User details`](#user-details) 21 - [`Edit user`](#edit-user) 22 - [`Manage user`](#manage-user) 23 - [`Users`](#users) 24 - [`Update user key`](#update-user-key) 25 - [`Verify update user key`](#verify-update-user-key) 26 - [`Change username`](#change-username) 27 - [`Change password`](#change-password) 28 - [`Reset password`](#reset-password) 29 - [`User proposal credits`](#user-proposal-credits) 30 - [`Proposal paywall details`](#proposal-paywall-details) 31 - [`Verify user payment`](#verify-user-payment) 32 - [`Rescan user payments`](#rescan-user-payments) 33 34 **Proposal Routes** 35 - [`Token inventory`](#token-inventory) 36 - [`Proposal details`](#proposal-details) 37 - [`Batch proposals`](#batch-proposals) 38 - [`Vote results`](#vote-results) 39 - [`Cast votes`](#cast-votes) 40 - [`Batch vote summary`](#batch-vote-summary) 41 42 43 **Error status codes** 44 45 - [`ErrorStatusInvalid`](#ErrorStatusInvalid) 46 - [`ErrorStatusInvalidPassword`](#ErrorStatusInvalidPassword) 47 - [`ErrorStatusMalformedEmail`](#ErrorStatusMalformedEmail) 48 - [`ErrorStatusVerificationTokenInvalid`](#ErrorStatusVerificationTokenInvalid) 49 - [`ErrorStatusVerificationTokenExpired`](#ErrorStatusVerificationTokenExpired) 50 - [`ErrorStatusProposalMissingFiles`](#ErrorStatusProposalMissingFiles) 51 - [`ErrorStatusProposalNotFound`](#ErrorStatusProposalNotFound) 52 - [`ErrorStatusProposalDuplicateFilenames`](#ErrorStatusProposalDuplicateFilenames) 53 - [`ErrorStatusProposalInvalidTitle`](#ErrorStatusProposalInvalidTitle) 54 - [`ErrorStatusMaxMDsExceededPolicy`](#ErrorStatusMaxMDsExceededPolicy) 55 - [`ErrorStatusMaxImagesExceededPolicy`](#ErrorStatusMaxImagesExceededPolicy) 56 - [`ErrorStatusMaxMDSizeExceededPolicy`](#ErrorStatusMaxMDSizeExceededPolicy) 57 - [`ErrorStatusMaxImageSizeExceededPolicy`](#ErrorStatusMaxImageSizeExceededPolicy) 58 - [`ErrorStatusMalformedPassword`](#ErrorStatusMalformedPassword) 59 - [`ErrorStatusCommentNotFound`](#ErrorStatusCommentNotFound) 60 - [`ErrorStatusInvalidFilename`](#ErrorStatusInvalidFilename) 61 - [`ErrorStatusInvalidFileDigest`](#ErrorStatusInvalidFileDigest) 62 - [`ErrorStatusInvalidBase64`](#ErrorStatusInvalidBase64) 63 - [`ErrorStatusInvalidMIMEType`](#ErrorStatusInvalidMIMEType) 64 - [`ErrorStatusUnsupportedMIMEType`](#ErrorStatusUnsupportedMIMEType) 65 - [`ErrorStatusInvalidPropStatusTransition`](#ErrorStatusInvalidPropStatusTransition) 66 - [`ErrorStatusInvalidPublicKey`](#ErrorStatusInvalidPublicKey) 67 - [`ErrorStatusNoPublicKey`](#ErrorStatusNoPublicKey) 68 - [`ErrorStatusInvalidSignature`](#ErrorStatusInvalidSignature) 69 - [`ErrorStatusInvalidInput`](#ErrorStatusInvalidInput) 70 - [`ErrorStatusInvalidSigningKey`](#ErrorStatusInvalidSigningKey) 71 - [`ErrorStatusCommentLengthExceededPolicy`](#ErrorStatusCommentLengthExceededPolicy) 72 - [`ErrorStatusUserNotFound`](#ErrorStatusUserNotFound) 73 - [`ErrorStatusWrongStatus`](#ErrorStatusWrongStatus) 74 - [`ErrorStatusNotLoggedIn`](#ErrorStatusNotLoggedIn) 75 - [`ErrorStatusUserNotPaid`](#ErrorStatusUserNotPaid) 76 - [`ErrorStatusReviewerAdminEqualsAuthor`](#ErrorStatusReviewerAdminEqualsAuthor) 77 - [`ErrorStatusMalformedUsername`](#ErrorStatusMalformedUsername) 78 - [`ErrorStatusDuplicateUsername`](#ErrorStatusDuplicateUsername) 79 - [`ErrorStatusVerificationTokenUnexpired`](#ErrorStatusVerificationTokenUnexpired) 80 - [`ErrorStatusCannotVerifyPayment`](#ErrorStatusCannotVerifyPayment) 81 - [`ErrorStatusDuplicatePublicKey`](#ErrorStatusDuplicatePublicKey) 82 - [`ErrorStatusInvalidPropVoteStatus`](#ErrorStatusInvalidPropVoteStatus) 83 - [`ErrorStatusNoProposalCredits`](#ErrorStatusNoProposalCredits) 84 - [`ErrorStatusInvalidUserManageAction`](#ErrorStatusInvalidUserManageAction) 85 - [`ErrorStatusUserActionNotAllowed`](#ErrorStatusUserActionNotAllowed) 86 - [`ErrorStatusWrongVoteStatus`](#ErrorStatusWrongVoteStatus) 87 - [`ErrorStatusCannotVoteOnPropComment`](#ErrorStatusCannotVoteOnPropComment) 88 - [`ErrorStatusChangeMessageCannotBeBlank`](#ErrorStatusChangeMessageCannotBeBlank) 89 - [`ErrorStatusCensorReasonCannotBeBlank`](#ErrorStatusCensorReasonCannotBeBlank) 90 - [`ErrorStatusCannotCensorComment`](#ErrorStatusCannotCensorComment) 91 - [`ErrorStatusUserNotAuthor`](#ErrorStatusUserNotAuthor) 92 - [`ErrorStatusVoteNotAuthorized`](#ErrorStatusVoteNotAuthorized) 93 - [`ErrorStatusVoteAlreadyAuthorized`](#ErrorStatusVoteAlreadyAuthorized) 94 - [`ErrorStatusInvalidAuthVoteAction`](#ErrorStatusInvalidAuthVoteAction) 95 - [`ErrorStatusUserDeactivated`](#ErrorStatusUserDeactivated) 96 - [`ErrorStatusInvalidPropVoteBits`](#ErrorStatusInvalidPropVoteBits) 97 - [`ErrorStatusInvalidPropVoteParams`](#ErrorStatusInvalidPropVoteParams) 98 - [`ErrorStatusEmailNotVerified`](#ErrorStatusEmailNotVerified) 99 - [`ErrorStatusInvalidUUID`](#ErrorStatusInvalidUUID) 100 - [`ErrorStatusInvalidLikeCommentAction`](#ErrorStatusInvalidLikeCommentAction) 101 - [`ErrorStatusInvalidCensorshipToken`](#ErrorStatusInvalidCensorshipToken) 102 - [`ErrorStatusEmailAlreadyVerified`](#ErrorStatusEmailAlreadyVerified) 103 - [`ErrorStatusNoProposalChanges`](#ErrorStatusNoProposalChanges) 104 - [`ErrorStatusMaxProposalsExceededPolicy`](#ErrorStatusMaxProposalsExceededPolicy) 105 - [`ErrorStatusDuplicateComment`](#ErrorStatusDuplicateComment) 106 - [`ErrorStatusInvalidLogin`](#ErrorStatusInvalidLogin) 107 - [`ErrorStatusCommentIsCensored`](#ErrorStatusCommentIsCensored) 108 - [`ErrorStatusInvalidProposalVersion`](#ErrorStatusInvalidProposalVersion) 109 - [`ErrorStatusMetadataInvalid`](#ErrorStatusMetadataInvalid) 110 - [`ErrorStatusMetadataMissing`](#ErrorStatusMetadataMissing) 111 - [`ErrorStatusMetadataDigestInvalid`](#ErrorStatusMetadataDigestInvalid) 112 - [`ErrorStatusInvalidVoteType`](#ErrorStatusInvalidVoteType) 113 - [`ErrorStatusInvalidVoteOptions`](#ErrorStatusInvalidVoteOptions) 114 - [`ErrorStatusLinkByDeadlineNotMet`](#ErrorStatusLinkByDeadlineNotMet) 115 - [`ErrorStatusNoLinkedProposals`](#ErrorStatusNoLinkedProposals) 116 - [`ErrorStatusInvalidLinkTo`](#ErrorStatusInvalidLinkTo) 117 - [`ErrorStatusInvalidLinkBy`](#ErrorStatusInvalidLinkBy) 118 - [`ErrorStatusInvalidRunoffVote`](#ErrorStatusInvalidRunoffVote) 119 - [`ErrorStatusWrongProposalType`](#ErrorStatusWrongProposalType) 120 121 **Websockets** 122 123 See [`Websocket command flow`](#Websocket-command-flow) for a generic 124 description of websocket command flow. 125 126 - [`WSError`](#WSError) 127 - [`WSHeader`](#WSHeader) 128 - [`WSPing`](#WSPing) 129 - [`WSSubscribe`](#WSSubscribe) 130 131 ## HTTP status codes and errors 132 133 All methods, unless otherwise specified, shall return `200 OK` when successful, 134 `400 Bad Request` when an error has occurred due to user input, or `500 135 Internal Server Error` when an unexpected server error has occurred. The format 136 of errors is as follows: 137 138 **`4xx` errors** 139 140 | | Type | Description | 141 |-|-|-| 142 | errorcode | number | One of the [error codes](#error-codes) | 143 | errorcontext | Array of Strings | This array of strings is used to provide additional information for certain errors; see the documentation for specific error codes. | 144 145 **`5xx` errors** 146 147 | | Type | Description | 148 |-|-|-| 149 | errorcode | number | An error code that can be used to track down the internal server error that occurred; it should be reported to Politeia administrators. | 150 151 ## Websocket command flow 152 153 There are two distinct websockets routes. There is an unauthenticated route and 154 an authenticated route. The authenticated route provides access to all 155 unprivileged websocket commands and therefore a client that authenticates 156 itself via the [`Login`](#login) call should close any open unprivileged 157 websockets. Note that sending notifications to unauthenticated users means 158 **ALL** unauthenticated users; this may be expensive and should be used 159 carefully. 160 161 All commands consist of two JSON structures. All commands are prefixed by a 162 [`WSHeader`](#WSHeader) structure that identifies the command that follows. 163 This is done to prevent decoding JSON multiple times. That structure also 164 contains a convenience field called **ID** which can be set by the client in 165 order to identify prior sent commands. 166 167 If a client command fails the server shall return a [`WSError`](#WSError) 168 structure, prefixed by a [`WSHeader`](#WSHeader) structure that contains the 169 client side **ID** followed by the error(s) itself. If there is no failure the 170 server does not reply. Note that **ID** is unused when server notifications 171 flow to the client. 172 173 Both routes operate exactly the same way. The only difference is denied access 174 to subscriptions of privileged notifications. 175 176 **Unauthenticated route**: `/v1/ws` 177 **Authenticated route**: `/v1/aws` 178 179 For example, a subscribe command consists of a [`WSHeader`](#WSHeader) 180 structure followed by a [`WSSubscribe`](#WSSubscribe) structure: 181 ``` 182 { 183 "command": "subscribe", 184 "id": "1" 185 } 186 { 187 "rpcs": [ 188 "ping" 189 ] 190 } 191 ``` 192 193 The same example but with an invalid subscription: 194 ``` 195 { 196 "command": "subscribe", 197 "id": "1" 198 } 199 { 200 "rpcs": [ 201 "pingo" 202 ] 203 } 204 ``` 205 206 Since **pingo** is an invalid subscription the server will reply with the 207 following error: 208 ``` 209 { 210 "command": "error", 211 "id": "1" 212 } 213 { 214 "command": "subscribe", 215 "id": "1", 216 "errors": [ 217 "invalid subscription pingo" 218 ] 219 } 220 ``` 221 222 ## Methods 223 224 ### `Version` 225 226 Obtain version, route information and signing identity from server. This call 227 shall **ALWAYS** be the first contact with the server. This is done in order 228 to get the CSRF token for the session and to ensure API compatibility. 229 230 **Route**: `GET /` and `GET /version` 231 232 **Params**: none 233 234 **Results**: 235 236 | | Type | Description | 237 |-|-|-| 238 | version | number | API version that is running on this server. | 239 | route | string | Route that should be prepended to all calls. For example, "/v1". | 240 | pubkey | string | The public key for the corresponding private key that signs various tokens to ensure server authenticity and to prevent replay attacks. | 241 | testnet | boolean | Value to inform either its running on testnet or not | 242 | mode | string | Current mode that politeiawww is running (possibly piwww or cmswww) | 243 | activeusersesstion | boolean | Indicates if there is an active user from the session or not | 244 245 **Example** 246 247 Request: 248 249 ```json 250 {} 251 ``` 252 253 Reply: 254 255 ```json 256 { 257 "version": 1, 258 "route": "/v1", 259 "pubkey": "99e748e13d7ecf70ef6b5afa376d692cd7cb4dbb3d26fa83f417d29e44c6bb6c", 260 "testnet": true, 261 "mode": "piwww", 262 "activeusersession": true 263 } 264 ``` 265 266 ### `Me` 267 268 Return pertinent user information of the current logged in user. 269 270 **Route**: `GET /v1/user/me` 271 272 **Params**: none 273 274 **Results**: See the [`Login reply`](#login-reply). 275 276 **Example** 277 278 Request: 279 280 ```json 281 {} 282 ``` 283 284 Reply: 285 286 ```json 287 { 288 "isadmin":false, 289 "userid":"12", 290 "email":"69af376cca42cd9c@example.com", 291 "publickey":"5203ab0bb739f3fc267ad20c945b81bcb68ff22414510c000305f4f0afb90d1b", 292 "paywalladdress":"Tsgs7qb1Gnc43D9EY3xx9ou8Lbo8rB7me6M", 293 "paywallamount": 10000000, 294 "paywalltxnotbefore": 1528821554 295 } 296 ``` 297 298 ### `New user` 299 300 Create a new user on the politeiawww server. 301 302 **Route:** `POST /v1/user/new` 303 304 **Params:** 305 306 | Parameter | Type | Description | Required | 307 |-|-|-|-| 308 | email | string | Email is used as the web site user identity for a user. When a user changes email addresses the server shall maintain a mapping between the old and new address. | Yes | 309 | username | string | Unique username that the user wishes to use. | Yes | 310 | password | string | The password that the user wishes to use. This password travels in the clear in order to enable JS-less systems. The server shall never store passwords in the clear. | Yes | 311 | publickey | string | User ed25519 public key. | Yes | 312 313 **Results:** 314 315 | Parameter | Type | Description | 316 |-|-|-| 317 | verificationtoken | String | The verification token which is required when calling [`Verify user`](#verify-user). If an email server is set up, this property will be empty or nonexistent; the token will be sent to the email address sent in the request.| 318 319 This call can return one of the following error codes: 320 321 - [`ErrorStatusMalformedEmail`](#ErrorStatusMalformedEmail) 322 - [`ErrorStatusMalformedUsername`](#ErrorStatusMalformedUsername) 323 - [`ErrorStatusDuplicateUsername`](#ErrorStatusDuplicateUsername) 324 - [`ErrorStatusMalformedPassword`](#ErrorStatusMalformedPassword) 325 - [`ErrorStatusInvalidPublicKey`](#ErrorStatusInvalidPublicKey) 326 - [`ErrorStatusDuplicatePublicKey`](#ErrorStatusDuplicatePublicKey) 327 328 The email shall include a link in the following format: 329 330 ``` 331 /user/verify?email=69af376cca42cd9c@example.com&verificationtoken=fc8f660e7f4d590e27e6b11639ceeaaec2ce9bc6b0303344555ac023ab8ee55f 332 ``` 333 334 The call may return `500 Internal Server Error` which is accompanied by 335 an error code that allows the server operator to correlate issues with user 336 reports. 337 338 * **Example** 339 340 Request: 341 342 ```json 343 { 344 "email": "69af376cca42cd9c@example.com", 345 "password": "69af376cca42cd9c", 346 "username": "foobar", 347 "publickey":"5203ab0bb739f3fc267ad20c945b81bcb68ff22414510c000305f4f0afb90d1b" 348 } 349 ``` 350 351 Reply: 352 353 ```json 354 { 355 "verificationtoken": "fc8f660e7f4d590e27e6b11639ceeaaec2ce9bc6b0303344555ac023ab8ee55f" 356 } 357 ``` 358 359 ### `Verify user` 360 361 Verify email address of a previously created user. 362 363 **Route:** `GET /v1/user/verify` 364 365 **Params:** 366 367 | Parameter | Type | Description | Required | 368 |-|-|-|-| 369 | email | string | Email address of previously created user. | Yes | 370 | verificationtoken | string | The token that was provided by email to the user. | Yes | 371 | signature | string | The ed25519 signature of the string representation of the verification token. | Yes | 372 373 **Results:** none 374 375 On success the call shall return `200 OK`. 376 377 On failure the call shall return `400 Bad Request` and one of the following error codes: 378 - [`ErrorStatusVerificationTokenInvalid`](#ErrorStatusVerificationTokenInvalid) 379 - [`ErrorStatusVerificationTokenExpired`](#ErrorStatusVerificationTokenExpired) 380 - [`ErrorStatusNoPublicKey`](#ErrorStatusNoPublicKey) 381 - [`ErrorStatusInvalidPublicKey`](#ErrorStatusInvalidPublicKey) 382 - [`ErrorStatusInvalidSignature`](#ErrorStatusInvalidSignature) 383 - [`ErrorStatusInvalidInput`](#ErrorStatusInvalidInput) 384 385 **Example:** 386 387 Request: 388 389 The request params should be provided within the URL: 390 391 ``` 392 /v1/user/verify?email=abc@example.com&verificationtoken=f1c2042d36c8603517cf24768b6475e18745943e4c6a20bc0001f52a2a6f9bde&signature=9e4b1018913610c12496ec3e482f2fb42129197001c5d35d4f5848b77d2b5e5071f79b18bcab4f371c5b378280bb478c153b696003ac3a627c3d8a088cd5f00d 393 ``` 394 395 Reply: 396 397 ```json 398 {} 399 ``` 400 401 ### `Resend verification` 402 403 Sends another verification email for a new user registration. 404 405 **Route:** `POST /v1/user/new/resend` 406 407 **Params:** 408 409 | Parameter | Type | Description | Required | 410 |-|-|-|-| 411 | email | string | Email address which was used to sign up. | Yes | 412 | publickey | string | User ed25519 public key. This can be the same key used to sign up or a new one. | Yes | 413 414 **Results:** 415 416 | Parameter | Type | Description | 417 |-|-|-| 418 | verificationtoken | String | The verification token which is required when calling [`Verify user`](#verify-user). If an email server is set up, this property will be empty or nonexistent; the token will be sent to the email address sent in the request.| 419 420 This call can return one of the following error codes: 421 422 - [`ErrorStatusInvalidPublicKey`](#ErrorStatusInvalidPublicKey) 423 - [`ErrorStatusDuplicatePublicKey`](#ErrorStatusDuplicatePublicKey) 424 425 The email shall include a link in the following format: 426 427 ``` 428 /user/verify?email=69af376cca42cd9c@example.com&verificationtoken=fc8f660e7f4d590e27e6b11639ceeaaec2ce9bc6b0303344555ac023ab8ee55f 429 ``` 430 431 The call may return `500 Internal Server Error` which is accompanied by 432 an error code that allows the server operator to correlate issues with user 433 reports. 434 435 * **Example** 436 437 Request: 438 439 ```json 440 { 441 "email": "69af376cca42cd9c@example.com", 442 "publickey":"5203ab0bb739f3fc267ad20c945b81bcb68ff22414510c000305f4f0afb90d1b" 443 } 444 ``` 445 446 Reply: 447 448 ```json 449 { 450 "verificationtoken": "fc8f660e7f4d590e27e6b11639ceeaaec2ce9bc6b0303344555ac023ab8ee55f" 451 } 452 ``` 453 454 ### `Login` 455 456 Login as a user or admin. Admin status is determined by the server based on 457 the user database. Note that Login reply is identical to Me reply. 458 459 A valid TOTP code is required if user has set and verified a TOTP secret 460 key previously. 461 462 **Route:** `POST /v1/login` 463 464 **Params:** 465 466 | Parameter | Type | Description | Required | 467 |-|-|-|-| 468 | email | string | Email address of user that is attempting to login. | Yes | 469 | password | string | Accompanying password for provided email. | Yes | 470 | code | string | TOTP code based on user's TOTP secret (if verified). | No | 471 472 **Results:** See the [`Login reply`](#login-reply). 473 474 On failure the call shall return `401 Unauthorized` and one of the following 475 error codes: 476 - [`ErrorStatusInvalidLogin`](#ErrorStatusInvalidLogin) 477 - [`ErrorStatusEmailNotVerified`](#ErrorStatusEmailNotVerified) 478 - [`ErrorStatusUserDeactivated`](#ErrorStatusUserDeactivated) 479 - [`ErrorStatusUserLocked`](#ErrorStatusUserLocked) 480 - [`ErrorStatusRequiresTOTPCode`](#ErrorStatusRequiresTOTPCode) 481 - [`ErrorStatusTOTPWaitForNewCode`](#ErrorStatusTOTPWaitForNewCode) 482 - [`ErrorStatusTOTPFailedValidation`](#ErrorStatusTOTPFailedValidation) 483 484 **Example** 485 486 Request: 487 488 ```json 489 { 490 "email":"26c5687daca2f5d8@example.com", 491 "password":"26c5687daca2f5d8" 492 } 493 ``` 494 495 Reply: 496 497 ```json 498 { 499 "isadmin":true, 500 "userid":"0", 501 "email":"26c5687daca2f5d8@example.com", 502 "publickey":"ec88b934fd9f334a9ed6d2e719da2bdb2061de5370ff20a38b0e1e3c9538199a", 503 "paywalladdress":"", 504 "paywallamount":"", 505 "paywalltxnotbefore":"" 506 } 507 ``` 508 509 ### `Logout` 510 511 Logout as a user or admin. 512 513 **Route:** `POST /v1/logout` 514 515 **Params:** none 516 517 **Results:** none 518 519 **Example** 520 521 Request: 522 523 ```json 524 {} 525 ``` 526 527 Reply: 528 529 ```json 530 {} 531 ``` 532 533 ### `Verify user payment` 534 535 Checks that a user has paid his user registration fee. 536 537 **Route:** `GET /v1/user/payments/registration` 538 539 **Params:** none 540 541 **Results:** 542 543 | Parameter | Type | Description | 544 |-|-|-| 545 | haspaid | boolean | Whether or not a transaction on the blockchain that was sent to the `paywalladdress` | 546 | paywalladdress | String | The address in which to send the transaction containing the `paywallamount`. If the user has already paid, this field will be empty or not present. | 547 | paywallamount | Int64 | The amount of DCR (in atoms) to send to `paywalladdress`. If the user has already paid, this field will be empty or not present. | 548 | paywalltxnotbefore | Int64 | The minimum UNIX time (in seconds) required for the block containing the transaction sent to `paywalladdress`. If the user has already paid, this field will be empty or not present. | 549 550 On failure the call shall return `400 Bad Request` and one of the following 551 error codes: 552 - [`ErrorStatusCannotVerifyPayment`](#ErrorStatusCannotVerifyPayment) 553 554 **Example** 555 556 Request: 557 558 ``` 559 /v1/user/payments/registration 560 ``` 561 562 Reply: 563 564 ```json 565 { 566 "haspaid": true, 567 "paywalladdress":"", 568 "paywallamount":"", 569 "paywalltxnotbefore":"" 570 } 571 ``` 572 573 ### `Rescan user payments` 574 575 Rescan payments made by the user. 576 577 **Route:** `GET /v1/user/payments/rescan` 578 579 **Params:** 580 | Parameter | Type | Description | Required | 581 |-----------|------|-------------|----------| 582 | userid | string | The unique id of the user. | Yes | 583 584 **Results:** 585 586 | Parameter | Type | Description | 587 |-|-|-| 588 | newcredits | []ProposalCredits | Contains information about the user's 589 proposal credits payments` | 590 591 On failure the call shall return `400 Bad Request` and one of the following 592 error codes: 593 - [`ErrorStatusInvalidInput`](#ErrorStatusInvalidInput) 594 595 **Example** 596 597 Request: 598 599 ``` 600 /v1/user/payments/rescan 601 ``` 602 Request: 603 604 ```json 605 { 606 "userid": "1" 607 } 608 ``` 609 610 Reply: 611 612 ```json 613 { 614 "newcredits": [ 615 { 616 "paywallid": 1, 617 "price": 10000000, 618 "datepurchased": 1528821554, 619 "txid": "ff0207a03b761cb409c7677c5b5521562302653d2236c92d016dd47e0ae37bf7", 620 }, 621 ] 622 } 623 ``` 624 625 ### `User details` 626 627 Returns details about a user given its id. Returns complete data if request is from 628 admin or own user, and omits private data if request is from a normal user or logged 629 out user. 630 631 **Route:** `GET /v1/user/{userid}` 632 633 **Params:** 634 635 | Parameter | Type | Description | Required | 636 |-----------|------|-------------|----------| 637 | userid | string | The unique id of the user. | Yes | 638 639 **Results:** 640 641 | Parameter | Type | Description | 642 |-|-|-| 643 | user | [User](#user) | The user details. | 644 645 On failure the call shall return `400 Bad Request` and one of the following 646 error codes: 647 - [`ErrorStatusUserNotFound`](#ErrorStatusUserNotFound) 648 649 **Example** 650 651 Request: 652 653 ```json 654 { 655 "userid": "0" 656 } 657 ``` 658 659 Reply: 660 661 For a logged in admin user or own user requesting data. 662 663 ```json 664 { 665 "user": { 666 "id": "0", 667 "email": "6b87b6ebb0c80cb7@example.com", 668 "username": "6b87b6ebb0c80cb7", 669 "isadmin": false, 670 "newuserpaywalladdress": "Tsgs7qb1Gnc43D9EY3xx9ou8Lbo8rB7me6M", 671 "newuserpaywallamount": 10000000, 672 "newuserpaywalltx": "", 673 "newuserpaywalltxnotbefore": 1528821554, 674 "newuserpaywallpollexpiry": 1528821554, 675 "newuserverificationtoken": 676 "337fc4762dac6bbe11d3d0130f33a09978004b190e6ebbbde9312ac63f223527", 677 "newuserverificationexpiry": 1528821554, 678 "updatekeyverificationtoken": 679 "337fc4762dac6bbe11d3d0130f33a09978004b190e6ebbbde9312ac63f223527", 680 "updatekeyverificationexpiry": 1528821554, 681 "resetpasswordverificationtoken": 682 "337fc4762dac6bbe11d3d0130f33a09978004b190e6ebbbde9312ac63f223527", 683 "resetpasswordverificationexpiry": 1528821554, 684 "lastlogintime": 1571316271, 685 "failedloginattemps": 3, 686 "isdeactivated": false, 687 "islocked": false, 688 "identities": [{ 689 "pubkey": 690 "5203ab0bb739f3fc267ad20c945b81bcb68ff22414510c000305f4f0afb90d1b", 691 "isactive": true 692 }], 693 "proposalCredits": 10, 694 "emailnotifications": 3 695 } 696 } 697 ``` 698 699 Reply: 700 701 For a unlogged or normal user requesting data. 702 703 ```json 704 { 705 "user": { 706 "id": "0", 707 "email": "", 708 "username": "6b87b6ebb0c80cb7", 709 "isadmin": false, 710 "newuserpaywalladdress": "", 711 "newuserpaywallamount": 0, 712 "newuserpaywalltx": "", 713 "newuserpaywalltxnotbefore": 0, 714 "newuserpaywallpollexpiry": 0, 715 "newuserverificationtoken": "", 716 "newuserverificationexpiry": 0, 717 "updatekeyverificationtoken": null, 718 "updatekeyverificationexpiry": 0, 719 "resetpasswordverificationtoken": null, 720 "resetpasswordverificationexpiry": 0, 721 "lastlogintime": 0, 722 "failedloginattemps": 0, 723 "isdeactivated": false, 724 "islocked": false, 725 "identities": [{ 726 "pubkey": 727 "5203ab0bb739f3fc267ad20c945b81bcb68ff22414510c000305f4f0afb90d1b", 728 "isactive": true 729 }], 730 "proposalCredits": 0, 731 "emailnotifications": 0 732 } 733 } 734 ``` 735 ### `Edit user` 736 737 Edits a user's details. This call requires login privileges. 738 739 **Route:** `POST /v1/user/edit` 740 741 **Params:** 742 743 | Parameter | Type | Description | Required | 744 |-----------|------|-------------|----------| 745 | emailnotifications | uint64 | The unique id of the user. | Yes | 746 747 **Results:** none 748 749 On failure the call shall return `400 Bad Request` and one of the following 750 error codes: 751 - [`ErrorStatusInvalidInput`](#ErrorStatusInvalidInput) 752 753 **Example** 754 755 Request: 756 757 ```json 758 { 759 "emailnotifications": 2, 760 } 761 ``` 762 763 Reply: 764 765 ```json 766 {} 767 ``` 768 769 ### `Manage user` 770 771 Edits a user's details. This call requires admin privileges. 772 773 **Route:** `POST /v1/user/manage` 774 775 **Params:** 776 777 | Parameter | Type | Description | Required | 778 |-----------|------|-------------|----------| 779 | userid | string | The unique id of the user. | Yes | 780 | action | int64 | The [user edit action](#user-edit-actions) to execute on the user. | Yes | 781 | reason | string | The admin's reason for executing this action. | Yes | 782 783 **Results:** none 784 785 On failure the call shall return `400 Bad Request` and one of the following 786 error codes: 787 - [`ErrorStatusUserNotFound`](#ErrorStatusUserNotFound) 788 - [`ErrorStatusInvalidInput`](#ErrorStatusInvalidInput) 789 - [`ErrorStatusInvalidUserManageAction`](#ErrorStatusInvalidUserManageAction) 790 791 **Example** 792 793 Request: 794 795 ```json 796 { 797 "userid": "0", 798 "action": 1 799 } 800 ``` 801 802 Reply: 803 804 ```json 805 {} 806 ``` 807 808 ### `Users` 809 810 Returns a list of users given optional filters. This call requires admin privileges. 811 812 **Route:** `GET /v1/users` 813 814 **Params:** 815 816 | Parameter | Type | Description | Required | 817 |-----------|------|-------------|----------| 818 | email | string | A query string to match against user email addresses. | | 819 | username | string | A query string to match against usernames. | | 820 821 **Results:** 822 823 | Parameter | Type | Description | 824 |-|-|-| 825 | totalusers | uint64 | The total number of all users in the database. | 826 | totalmatches | uint64 | The total number of users that matched the query. | 827 | users | array of [Abridged User](#abridged-user) | The list of users that match the query. This list will be capped at the `userlistpagesize`, which is specified in the [`Policy`](#policy) call. | 828 829 On failure the call shall return `400 Bad Request` and one of the following 830 error codes: 831 - [`ErrorStatusInvalidInput`](#ErrorStatusInvalidInput) 832 833 **Example** 834 835 Request: 836 837 ```json 838 { 839 "email": "@aol.com", 840 "username": "JakeFromStateFarm" 841 } 842 ``` 843 844 Reply: 845 846 ```json 847 { 848 "totalusers": 132, 849 "totalmatches": 0, 850 "users": [] 851 } 852 ``` 853 854 ### `Update user key` 855 856 Updates the user's active key pair. 857 858 **Route:** `POST /v1/user/key` 859 860 **Params:** 861 862 | Parameter | Type | Description | Required | 863 |-|-|-|-| 864 | publickey | string | User's new active ed25519 public key. | Yes | 865 866 **Results:** 867 868 | Parameter | Type | Description | 869 |-|-|-| 870 | verificationtoken | String | The verification token which is required when calling [`Verify update user key`](#verify-update-user-key). If an email server is set up, this property will be empty or nonexistent; the token will be sent to the email address sent in the request. | 871 872 This call can return one of the following error codes: 873 874 - [`ErrorStatusInvalidPublicKey`](#ErrorStatusInvalidPublicKey) 875 - [`ErrorStatusVerificationTokenUnexpired`](#ErrorStatusVerificationTokenUnexpired) 876 877 The email shall include a link in the following format: 878 879 ``` 880 /v1/user/key/verify?verificationtoken=fc8f660e7f4d590e27e6b11639ceeaaec2ce9bc6b0303344555ac023ab8ee55 881 ``` 882 883 The call may return `500 Internal Server Error` which is accompanied by 884 an error code that allows the server operator to correlate issues with user 885 reports. 886 887 * **Example** 888 889 Request: 890 891 ```json 892 { 893 "publickey":"5203ab0bb739f3fc267ad20c945b81bcb68ff22414510c000305f4f0afb90d1b" 894 } 895 ``` 896 897 Reply: 898 899 ```json 900 { 901 "verificationtoken": "fc8f660e7f4d590e27e6b11639ceeaaec2ce9bc6b0303344555ac023ab8ee55f" 902 } 903 ``` 904 905 ### `Verify update user key` 906 907 Verify the new key pair for the user. 908 909 **Route:** `POST /v1/user/key/verify` 910 911 **Params:** 912 913 | Parameter | Type | Description | Required | 914 |-|-|-|-| 915 | verificationtoken | string | The token that was provided by email to the user. | Yes | 916 | signature | string | The ed25519 signature of the string representation of the verification token. | Yes | 917 918 **Results:** none 919 920 On success the call shall return `200 OK`. 921 922 On failure the call shall return `400 Bad Request` and one of the following error codes: 923 - [`ErrorStatusVerificationTokenInvalid`](#ErrorStatusVerificationTokenInvalid) 924 - [`ErrorStatusVerificationTokenExpired`](#ErrorStatusVerificationTokenExpired) 925 - [`ErrorStatusNoPublicKey`](#ErrorStatusNoPublicKey) 926 - [`ErrorStatusInvalidPublicKey`](#ErrorStatusInvalidPublicKey) 927 - [`ErrorStatusInvalidSignature`](#ErrorStatusInvalidSignature) 928 - [`ErrorStatusInvalidInput`](#ErrorStatusInvalidInput) 929 930 **Example:** 931 932 Request: 933 934 The request params should be provided within the URL: 935 936 ```json 937 { 938 "verificationtoken":"f1c2042d36c8603517cf24768b6475e18745943e4c6a20bc0001f52a2a6f9bde", 939 "signature":"9e4b1018913610c12496ec3e482f2fb42129197001c5d35d4f5848b77d2b5e5071f79b18bcab4f371c5b378280bb478c153b696003ac3a627c3d8a088cd5f00d" 940 } 941 ``` 942 943 Reply: 944 945 ```json 946 {} 947 ``` 948 949 ### `Change username` 950 951 Changes the username for the currently logged in user. 952 953 **Route:** `POST /v1/user/username/change` 954 955 **Params:** 956 957 | Parameter | Type | Description | Required | 958 |-|-|-|-| 959 | password | string | The current password of the logged in user. | Yes | 960 | newusername | string | The new username for the logged in user. | Yes | 961 962 **Results:** none 963 964 On failure the call shall return `400 Bad Request` and one of the following 965 error codes: 966 - [`ErrorStatusInvalidPassword`](#ErrorStatusInvalidPassword) 967 - [`ErrorStatusMalformedUsername`](#ErrorStatusMalformedUsername) 968 - [`ErrorStatusDuplicateUsername`](#ErrorStatusDuplicateUsername) 969 970 **Example** 971 972 Request: 973 974 ```json 975 { 976 "password": "15a1eb6de3681fec", 977 "newusername": "foobar" 978 } 979 ``` 980 981 Reply: 982 983 ```json 984 {} 985 ``` 986 987 ### `Change password` 988 989 Changes the password for the currently logged in user. 990 991 **Route:** `POST /v1/user/password/change` 992 993 **Params:** 994 995 | Parameter | Type | Description | Required | 996 |-|-|-|-| 997 | currentpassword | string | The current password of the logged in user. | Yes | 998 | newpassword | string | The new password for the logged in user. | Yes | 999 1000 **Results:** none 1001 1002 On failure the call shall return `400 Bad Request` and one of the following 1003 error codes: 1004 - [`ErrorStatusInvalidPassword`](#ErrorStatusInvalidPassword) 1005 - [`ErrorStatusMalformedPassword`](#ErrorStatusMalformedPassword) 1006 1007 **Example** 1008 1009 Request: 1010 1011 ```json 1012 { 1013 "currentpassword": "15a1eb6de3681fec", 1014 "newpassword": "cef1863ed6be1a51" 1015 } 1016 ``` 1017 1018 Reply: 1019 1020 ```json 1021 {} 1022 ``` 1023 1024 ### `Reset password` 1025 1026 Allows a user to reset his password without being logged in. 1027 1028 **Route:** `POST /v1/user/password/reset` 1029 1030 **Params:** 1031 1032 | Parameter | Type | Description | Required | 1033 |-|-|-|-| 1034 | email | string | The email of the user whose password should be reset. | Yes | 1035 | verificationtoken | string | The verification token which is sent to the user's email address. | Yes | 1036 | newpassword | String | The new password for the user. | Yes | 1037 1038 **Results:** 1039 1040 | Parameter | Type | Description | 1041 |-|-|-| 1042 | verificationtoken | String | This command is special because it has to be called twice, the 2nd time the caller needs to supply the `verificationtoken` | 1043 1044 1045 The reset password command is special. It must be called **twice** with different 1046 parameters. 1047 1048 For the 1st call, it should be called with only an `email` parameter. On success 1049 it shall send an email to the address provided by `email` and return `200 OK`. 1050 1051 The email shall include a link in the following format: 1052 1053 ``` 1054 /v1/user/password/reset?email=abc@example.com&verificationtoken=f1c2042d36c8603517cf24768b6475e18745943e4c6a20bc0001f52a2a6f9bde 1055 ``` 1056 1057 On failure, the call shall return `400 Bad Request` and one of the following 1058 error codes: 1059 - [`ErrorStatusMalformedEmail`](#ErrorStatusMalformedEmail) 1060 1061 For the 2nd call, it should be called with `email`, `token`, and `newpassword` 1062 parameters. 1063 1064 On failure, the call shall return `400 Bad Request` and one of the following 1065 error codes: 1066 - [`ErrorStatusVerificationTokenInvalid`](#ErrorStatusVerificationTokenInvalid) 1067 - [`ErrorStatusVerificationTokenExpired`](#ErrorStatusVerificationTokenExpired) 1068 - [`ErrorStatusMalformedPassword`](#ErrorStatusMalformedPassword) 1069 1070 **Example for the 1st call** 1071 1072 Request: 1073 1074 ```json 1075 { 1076 "email": "6b87b6ebb0c80cb7@example.com" 1077 } 1078 ``` 1079 1080 Reply: 1081 1082 ```json 1083 {} 1084 ``` 1085 1086 **Example for the 2nd call** 1087 1088 Request: 1089 1090 ```json 1091 { 1092 "email": "6b87b6ebb0c80cb7@example.com", 1093 "verificationtoken": "f1c2042d36c8603517cf24768b6475e18745943e4c6a20bc0001f52a2a6f9bde", 1094 "newpassword": "6b87b6ebb0c80cb7" 1095 } 1096 ``` 1097 1098 Reply: 1099 1100 ```json 1101 { 1102 "verificationtoken": "f1c2042d36c8603517cf24768b6475e18745943e4c6a20bc0001f52a2a6f9bde" 1103 } 1104 ``` 1105 1106 ### `Proposal paywall details` 1107 Retrieve paywall details that can be used to purchase proposal credits. 1108 Proposal paywalls are only valid for one tx. The user can purchase as many 1109 proposal credits as they would like with that one tx. Proposal paywalls expire 1110 after a set duration. To verify that a payment has been made, 1111 politeiawww polls the paywall address until the paywall is either paid or it 1112 expires. A proposal paywall cannot be generated until the user has paid their 1113 user registration fee. 1114 1115 **Route:** `GET /v1/user/payments/paywall` 1116 1117 **Params:** none 1118 1119 **Results:** 1120 1121 | Parameter | Type | Description | 1122 |-|-|-| 1123 | creditprice | uint64 | Price per proposal credit in atoms. | 1124 | paywalladdress | string | Proposal paywall address. | 1125 | paywalltxnotbefore | string | Minimum timestamp for paywall tx. | 1126 On failure the call shall return `400 Bad Request` and one of the following 1127 error codes: 1128 - [`ErrorStatusUserNotPaid`](#ErrorStatusUserNotPaid) 1129 1130 **Example** 1131 1132 Request: 1133 1134 ```json 1135 {} 1136 ``` 1137 1138 Reply: 1139 1140 ```json 1141 { 1142 "creditprice": 10000000, 1143 "paywalladdress": "TsRBnD2mnZX1upPMFNoQ1ckYr9Y4TZyuGTV", 1144 "paywalltxnotbefore": 1532445975 1145 } 1146 ``` 1147 1148 ### `Proposal paywall tx details` 1149 Retrieve paywall details that can be used to purchase proposal credits. 1150 Proposal paywalls are only valid for one tx. The user can purchase as many 1151 proposal credits as they would like with that one tx. Proposal paywalls expire 1152 after a set duration. To verify that a payment has been made, 1153 politeiawww polls the paywall address until the paywall is either paid or it 1154 expires. A proposal paywall cannot be generated until the user has paid their 1155 user registration fee. 1156 1157 **Route:** `GET /v1/user/payments/paywalltx` 1158 1159 **Params:** 1160 | Parameter | Type | Description | Required | 1161 |-----------|------|-------------|----------| 1162 | userid | string | The unique id of the user. | Yes | 1163 1164 **Results:** 1165 1166 | Parameter | Type | Description | 1167 |-|-|-| 1168 | txid | string | Transaction id. | 1169 | txamount | uint64 | Transaction amount in atoms. | 1170 | confirmations | uint64 | Confirmations the transaction had on the network | 1171 1172 **Example** 1173 1174 Request: 1175 1176 ```json 1177 { 1178 "userid": "1" 1179 } 1180 ``` 1181 1182 Reply: 1183 1184 ```json 1185 { 1186 "txid": "ff0207a03b761cb409c7677c5b5521562302653d2236c92d016dd47e0ae37bf7", 1187 "txamount": 10000000, 1188 "confirmations": 2 1189 } 1190 ``` 1191 1192 ### `User proposal credits` 1193 Request a list of the user's unspent and spent proposal credits. 1194 1195 **Route:** `GET /v1/user/payments/credits` 1196 1197 **Params:** none 1198 1199 **Results:** 1200 1201 | Parameter | Type | Description | 1202 |-|-|-| 1203 | unspentcredits | array of [`ProposalCredit`](#proposal-credit)'s | The user's unspent proposal credits | 1204 | spentcredits | array of [`ProposalCredit`](#proposal-credit)'s | The user's spent proposal credits | 1205 1206 **Example** 1207 1208 Request: 1209 1210 ```json 1211 {} 1212 ``` 1213 1214 Reply: 1215 1216 ```json 1217 { 1218 "unspentcredits": [{ 1219 "paywallid": 2, 1220 "price": 10000000, 1221 "datepurchased": 1532438228, 1222 "txid": "ff0207a03b761cb409c7677c5b5521562302653d2236c92d016dd47e0ae37bf7" 1223 }], 1224 "spentcredits": [{ 1225 "paywallid": 1, 1226 "price": 10000000, 1227 "datepurchased": 1532437363, 1228 "txid": "1b6df077a0a745314dab58887c56c4261270bb7a809692fad8157a99a0c46477" 1229 }] 1230 } 1231 ``` 1232 1233 ### `Policy` 1234 1235 Retrieve server policy. The returned values contain various maxima that the 1236 client SHALL observe. 1237 1238 **Route:** `GET /v1/policy` 1239 1240 **Params:** none 1241 1242 **Results:** 1243 1244 | | Type | Description | 1245 |-|-|-| 1246 | minpasswordlength | number | minimum number of characters accepted for user passwords | 1247 | minusernamelength | number | minimum number of characters accepted for username | 1248 | maxusernamelength | number | maximum number of characters accepted for username | 1249 | usernamesupportedchars | array of strings | the regular expression of a valid username | 1250 | paywallenabled | bool | is paywall enabled | 1251 | proposallistpagesize | number | maximum number of proposals returned for the routes that return lists of proposals | 1252 | userlistpagesize | number | maximum number of users returned for the routes that return lists of users | 1253 | maximages | number | maximum number of images accepted when creating a new proposal | 1254 | maximagesize | number | maximum image file size (in bytes) accepted when creating a new proposal | 1255 | maxmds | number | maximum number of markdown files accepted when creating a new proposal | 1256 | maxmdsize | number | maximum markdown file size (in bytes) accepted when creating a new proposal | 1257 | validmimetypes | array of strings | list of all acceptable MIME types that can be communicated between client and server. | 1258 | maxproposalnamelength | number | max length of a proposal name | 1259 | minproposalnamelength | number | min length of a proposal name | 1260 | proposalnamesupportedchars | array of strings | the regular expression of a valid proposal name | 1261 | maxcommentlength | number | maximum number of characters accepted for comments | 1262 | backendpublickey | string | | 1263 | tokenprefixlength | number | The length of token prefix needed 1264 | buildinformation | []string | build information including module commit hashes | 1265 | IndexFilename | string | required filename for the proposal index.md file | 1266 | MinLinkbyPeriod | number | Minimum required period, in seconds, for the proposal linkby period | 1267 | MaxLinkByPeriod | number | Maximum allowed period, in seconds, for the proposal linkby period | 1268 | MinVoteDuration | number | Minimum allowed vote duration | 1269 | MaxVoteDuration | number | Maximum allowed vote duration | 1270 1271 **Example** 1272 1273 Request: 1274 1275 ``` 1276 /v1/policy 1277 ``` 1278 1279 Reply: 1280 1281 ```json 1282 { 1283 "minpasswordlength": 8, 1284 "minusernamelength": 3, 1285 "maxusernamelength": 30, 1286 "usernamesupportedchars": [ 1287 "A-z", "0-9", ".", ":", ";", ",", "-", " ", "@", "+" 1288 ], 1289 "proposallistpagesize": 20, 1290 "maximages": 5, 1291 "maximagesize": 524288, 1292 "maxmds": 1, 1293 "maxmdsize": 524288, 1294 "validmimetypes": [ 1295 "image/png", 1296 "text/plain", 1297 "text/plain; charset=utf-8" 1298 ], 1299 "paywallenabled": true, 1300 "proposalnamesupportedchars": [ 1301 "A-z", "0-9", "&", ".", ":", ";", ",", "-", " ", "@", "+", "#" 1302 ], 1303 "maxcommentlength": 8000, 1304 "backendpublickey": "", 1305 "minproposalnamelength": 8, 1306 "maxproposalnamelength": 80, 1307 "tokenprefixlength": 7, 1308 "minvoteduration": 2016, 1309 "maxvoteduration": 4032 1310 } 1311 ``` 1312 1313 ### `Proposal details` 1314 1315 Retrieve proposal and its details. This request can be made with the full 1316 censorship token or its 7 character prefix. This route will return only 1317 vetted proposals. 1318 1319 **Routes:** `GET /v1/proposals/{token}` 1320 1321 **Params:** 1322 1323 | Parameter | Type | Description | Required | 1324 |-|-|-|-| 1325 | token | string | Token is the unique censorship token that identifies a specific proposal. | Yes | 1326 | version | string | Proposal Version. The latest version is the default when no version is specified. | No | 1327 1328 **Results:** 1329 1330 | | Type | Description | 1331 |-|-|-| 1332 | proposal | [`Proposal`](#proposal) | The proposal with the provided token. | 1333 1334 On failure the call shall return `400 Bad Request` and one of the following 1335 error codes: 1336 - [`ErrorStatusProposalNotFound`](#ErrorStatusProposalNotFound) 1337 1338 **Example** 1339 1340 Request: 1341 1342 The request params should be provided within the URL: 1343 1344 ``` 1345 /v1/proposals/f1c2042d36c8603517cf24768b6475e18745943e4c6a20bc0001f52a2a6f9bde?version=2 1346 ``` 1347 1348 Reply: 1349 1350 ```json 1351 { 1352 "proposal": { 1353 "name": "My Proposal", 1354 "status": 3, 1355 "timestamp": 1508146426, 1356 "version": 2, 1357 "files": [{ 1358 "name": "index.md", 1359 "mime": "text/plain; charset=utf-8", 1360 "digest": "0dd10219cd79342198085cbe6f737bd54efe119b24c84cbc053023ed6b7da4c8", 1361 "payload": "VGhpcyBpcyBhIGRlc2NyaXB0aW9u" 1362 }], 1363 "censorshiprecord": { 1364 "token": "c378e0735b5650c9e79f70113323077b107b0d778547f0d40592955668f21ebf", 1365 "merkle": "0dd10219cd79342198085cbe6f737bd54efe119b24c84cbc053023ed6b7da4c8", 1366 "signature": "f5ea17d547d8347a2f2d77edcb7e89fcc96613d7aaff1f2a26761779763d77688b57b423f1e7d2da8cd433ef2cfe6f58c7cf1c43065fa6716a03a3726d902d0a" 1367 } 1368 } 1369 } 1370 ``` 1371 1372 ### `Batch proposals` 1373 1374 Retrieve the proposal details for a list of proposals. This route wil not 1375 return the proposal files. The number of proposals that may be requested is 1376 limited by the `ProposalListPageSize` property, which is provided via 1377 [`Policy`](#policy). This route will return only vetted proposals. 1378 1379 **Routes:** `POST /v1/proposals/batch` 1380 1381 **Params:** 1382 1383 | Parameter | Type | Description | Required | 1384 |-|-|-|-| 1385 | tokens | []string | Array of censorship tokens of the proposals to be retrieved | Yes | 1386 1387 **Results:** 1388 1389 | Parameter | Type | Description | 1390 |-|-|-| 1391 | proposals | [][`Proposal`](#proposal) | Array of proposals | 1392 1393 On failure the call shall return `400 Bad Request` and one of the following 1394 error codes: 1395 - [`ErrorStatusMaxProposalsExceededPolicy`](#ErrorStatusMaxProposalsExceededPolicy) 1396 - [`ErrorStatusInvalidCensorshipToken`](#ErrorStatusInvalidCensorshipToken) 1397 - [`ErrorStatusProposalNotFound`](#ErrorStatusProposalNotFound) 1398 1399 **Example** 1400 1401 Request: 1402 1403 ``` 1404 /v1/proposals/batch 1405 ``` 1406 1407 ```json 1408 { 1409 "tokens": [ 1410 "c9aaf64f9474a0c2aa2227363e3ba575e1926acd4257deba42dc6d5ab85f2cd2", 1411 "f08dc22069f854856e27a6cb107e10064a85b85b2a4db41755d54f90bd30b84f" 1412 ] 1413 } 1414 ``` 1415 1416 Reply: 1417 1418 ```json 1419 { 1420 "proposals": [ 1421 { 1422 "name": "Sample proposal 1", 1423 "state": 2, 1424 "status": 4, 1425 "timestamp": 1561637933, 1426 "userid": "bda3852b-f9e8-49a3-924a-147303b7d6b8", 1427 "username": "username", 1428 "publickey": "e008f83793c023321d54f283698c47fb50083489501a8c3b4b020b7c92930cb9", 1429 "signature": "9c05c50b67c74d7c7e80be702afe123f46ddb417583bcd97674073d0d5ddc35804bec88b01d158bc1ab89bdb21e3cabbb4f290365c375ca226f8652d8dc01602", 1430 "files": [], 1431 "numcomments": 0, 1432 "version": "1", 1433 "publishedat": 1561637933, 1434 "censorshiprecord": { 1435 "token": "c9aaf64f9474a0c2aa2227363e3ba575e1926acd4257deba42dc6d5ab85f2cd2", 1436 "merkle": "ab7d4fe5d89a1110b0c684d89a48558efaeb0247d13ba8a79200d7fdbde91559", 1437 "signature": "8860999e0df2b9b7cc727f2ebc6c32fd26a8c9bb7660524fefbf85202ea4e1296699544acdcc723d70708f9f1561007bac1c4d250eb5aa5ebdecea224a8fd105" 1438 } 1439 }, 1440 { 1441 "name": "Sample Proposal 2", 1442 "state": 2, 1443 "status": 4, 1444 "timestamp": 1560824670, 1445 "userid": "6bd802af-42cc-47af-b1dc-412f93f21689", 1446 "username": "user2", 1447 "publickey": "ceaca7ba3579620968a1720e0748f3005802a2fd9e5afe0c7916f79c70234664", 1448 "signature": "ea41d5f8808892488185d18447f5b8c9d77c0d65932464d5742c1d22dbb0c975b42aaddcb1dae4f5d4a4423f4965c8af4a9273d48faae1f0531abe3039608001", 1449 "files": [], 1450 "numcomments": 3, 1451 "version": "1", 1452 "publishedat": 1560824670, 1453 "censorshiprecord": { 1454 "token": "f08dc22069f854856e27a6cb107e10064a85b85b2a4db41755d54f90bd30b84f", 1455 "merkle": "34745ec2aee7ba0bf3111c66f8484efb32bfea3bfe6cdcc46420adc1c7d181cc", 1456 "signature": "f0638afa9466ec3e4954f64e77929a0dd22d05b685180eaf36816e6cd65237760d4485be8afee04b824665acc809856aeabe9eade48f23a99b7be42e4508ac05" 1457 } 1458 } 1459 ] 1460 } 1461 ``` 1462 1463 ### `Batch vote summary` 1464 1465 Retrieve the vote summaries for a list of proposals. The number of vote 1466 summaries that may be requested is limited by the `ProposalListPageSize` 1467 property, which is provided via [`Policy`](#policy). 1468 1469 **Routes:** `POST /v1/proposals/batchvotesummary` 1470 1471 **Params:** 1472 1473 | Parameter | Type | Description | Required | 1474 |-|-|-|-| 1475 | tokens | []string | Array of censorship tokens of the requested vote summaries | Yes | 1476 1477 **Results:** 1478 1479 | Parameter | Type | Description | 1480 |-|-|-| 1481 | bestblock | uint64 | Current block height | 1482 | summaries | map[string][`VoteSummary`](#vote-summary) | Map of [token]VoteSummary | 1483 1484 On failure the call shall return `400 Bad Request` on the following error code: 1485 - [`ErrorStatusMaxProposalsExceededPolicy`](#ErrorStatusMaxProposalsExceededPolicy) 1486 - [`ErrorStatusInvalidCensorshipToken`](#ErrorStatusInvalidCensorshipToken) 1487 - [`ErrorStatusProposalNotFound`](#ErrorStatusProposalNotFound) 1488 1489 **Example** 1490 1491 Request: 1492 1493 ``` 1494 /v1/proposals/batchvotesummary 1495 ``` 1496 1497 ```json 1498 { 1499 "tokens": [ 1500 "f08dc22069f854856e27a6cb107e10064a85b85b2a4db41755d54f90bd30b84f", 1501 "c9aaf64f9474a0c2aa2227363e3ba575e1926acd4257deba42dc6d5ab85f2cd2" 1502 ] 1503 } 1504 ``` 1505 1506 Reply: 1507 1508 ```json 1509 { 1510 "bestblock": 243994, 1511 "summaries": { 1512 "f08dc22069f854856e27a6cb107e10064a85b85b2a4db41755d54f90bd30b84f": { 1513 "status": 4, 1514 "eligibletickets": 5267, 1515 "duration": 2016, 1516 "endheight": 231614, 1517 "quorumpercentage": 20, 1518 "passpercentage": 60, 1519 "results": [ 1520 { 1521 "option": { 1522 "id": "no", 1523 "description": "Don't approve proposal", 1524 "bits": 1 1525 }, 1526 "votesreceived": 3 1527 }, 1528 { 1529 "option": { 1530 "id": "yes", 1531 "description": "Approve proposal", 1532 "bits": 2 1533 }, 1534 "votesreceived": 2 1535 } 1536 ] 1537 }, 1538 "c9aaf64f9474a0c2aa2227363e3ba575e1926acd4257deba42dc6d5ab85f2cd2": { 1539 "status": 4, 1540 "eligibletickets": 5270, 1541 "duration": 2016, 1542 "endheight": 229602, 1543 "quorumpercentage": 20, 1544 "passpercentage": 60, 1545 "results": [ 1546 { 1547 "option": { 1548 "id": "no", 1549 "description": "Don't approve proposal", 1550 "bits": 1 1551 }, 1552 "votesreceived": 1 1553 }, 1554 { 1555 "option": { 1556 "id": "yes", 1557 "description": "Approve proposal", 1558 "bits": 2 1559 }, 1560 "votesreceived": 4 1561 } 1562 ] 1563 } 1564 } 1565 } 1566 ``` 1567 1568 ### `Cast votes` 1569 1570 This is a batched call that casts multiple votes to multiple proposals. 1571 1572 Note that the webserver does not interpret the plugin structures. These are 1573 forwarded as-is to the politeia daemon. 1574 1575 **Route:** `POST /v1/proposals/castvotes` 1576 1577 **Params:** 1578 1579 | Parameter | Type | Description | Required | 1580 |-|-|-|-| 1581 | votes | array of CastVote | All votes | Yes | 1582 1583 **CastVote:** 1584 1585 | | Type | Description | 1586 | - | - | - | 1587 | token | string | Censorship token | 1588 | ticket | string | Ticket hash | 1589 | votebit | string | String encoded vote bit | 1590 | signature | string | signature of Token+Ticket+VoteBit | 1591 1592 **Results:** 1593 1594 | | Type | Description | 1595 | - | - | - | 1596 | receipts | array of CastVoteReply | Receipts for all cast votes. This appears in the same order and index as the votes that went in. | 1597 1598 **CastVoteReply:** 1599 1600 | | Type | Description | 1601 | - | - | - | 1602 | clientsignature | string | Signature that was sent in via CastVote | 1603 | signature | string | Signature of ClientSignature | 1604 | error | string | Error, "" if there was no error | 1605 1606 **Example** 1607 1608 Request: 1609 1610 ``` json 1611 { 1612 "votes": [{ 1613 "token":"642eb2f3798090b3234d8787aaba046f1f4409436d40994643213b63cb3f41da", 1614 "ticket":"1257089bfa5223739c27dd10150de71962442f57ee176389c79932c22536b31b", 1615 "votebit":"2", 1616 "signature":"1f05c95fd0c59b0ee68733bbc645437124702e2af40fe37f01f15784a161b8ebae432fcfc5c9388e8f7409e6f02976182eda3bffa5df5de968f40faf2d993a9992" 1617 },{ 1618 "token":"642eb2f3798090b3234d8787aaba046f1f4409436d40994643213b63cb3f41da", 1619 "ticket":"1c1e0b6968813f8321e721598f9510afae6acaa8576b64297e34fd5777d8d417", 1620 "votebit":"2", 1621 "signature":"1ff92d0025ea7ff283e4991b6fcdd6c87958f5ba5ba34863c075650a8b16dc23906f639ab83d034d6146de109afca7c0c92a00c60f36640846f679fb6ff2d7f966" 1622 } 1623 ] 1624 } 1625 ``` 1626 1627 Reply: 1628 1629 ```json 1630 { 1631 "receipts": [{ 1632 "clientsignature":"1f05c95fd0c59b0ee68733bbc645437124702e2af40fe37f01f15784a161b8ebae432fcfc5c9388e8f7409e6f02976182eda3bffa5df5de968f40faf2d993a9992", 1633 "signature":"1bc19bf3ee2da7b0a9a54ae944e42e7b9e8953fce0c122b0a0a540e900535ea7ae3c5f2bba8266025d797b0dd4e37f0d21ed2f974b246528ae162a3719ed0808", 1634 "error":"" 1635 },{ 1636 "clientsignature":"1ff92d0025ea7ff283e4991b6fcdd6c87958f5ba5ba34863c075650a8b16dc23906f639ab83d034d6146de109afca7c0c92a00c60f36640846f679fb6ff2d7f966", 1637 "signature":"dbd24b1205c3c81a1d8a5736d769e1d6fd37ea517c15934e4b2042df65567e8c4029137eec8fb03fdcf40ecfe5a5eaa2bd36f485c6597328f543d5c283de5e0a", 1638 "error":"" 1639 }] 1640 } 1641 ``` 1642 1643 ### `Vote results` 1644 1645 Retrieve vote results for a specified censorship token. If the voting period 1646 has not yet started for the given proposal a reply is returned with all fields 1647 set to their zero value. 1648 1649 Note that the webserver does not interpret the plugin structures. These are 1650 forwarded as-is to the politeia daemon. 1651 1652 **Route:** `GET /v1/proposals/{token}/votes` 1653 1654 **Params:** none 1655 1656 **Results:** 1657 1658 | | Type | Description | 1659 | - | - | - | 1660 | vote | Vote | Vote details | 1661 | castvotes | array of CastVote | Cast vote details | 1662 | startvotereply | StartVoteReply | Vote details (eligible tickets, start block etc) | 1663 1664 1665 On failure the call shall return `400 Bad Request` and one of the following 1666 error codes: 1667 - [`ErrorStatusProposalNotFound`](#ErrorStatusProposalNotFound) 1668 - [`ErrorStatusWrongStatus`](#ErrorStatusWrongStatus) 1669 1670 **Example** 1671 1672 Request: 1673 `GET /V1/proposals/642eb2f3798090b3234d8787aaba046f1f4409436d40994643213b63cb3f41da/votes` 1674 1675 1676 Reply: 1677 1678 ```json 1679 { 1680 "vote": { 1681 "token":"642eb2f3798090b3234d8787aaba046f1f4409436d40994643213b63cb3f41da", 1682 "mask":3, 1683 "duration":2016, 1684 "Options": [{ 1685 "id":"no", 1686 "description":"Don't approve proposal", 1687 "bits":1 1688 },{ 1689 "id":"yes", 1690 "description":"Approve proposal", 1691 "bits":2 1692 }] 1693 }, 1694 "castvotes": [{ 1695 "token":"642eb2f3798090b3234d8787aaba046f1f4409436d40994643213b63cb3f41da", 1696 "ticket":"91832123c3f04c0783fb51d93bffd6f641ce3e951c30a29e15fb9986f23817c0", 1697 "votebit":"2", 1698 "signature":"208e614662fd7719df82687b72578cfb1f5e54fd05287e67683397b77e1819d4ff5c2029117d1d01bfa5c4637b7661ad95319f455c264ed4b4637382ffee5d5d9e" 1699 },{ 1700 "token":"642eb2f3798090b3234d8787aaba046f1f4409436d40994643213b63cb3f41da", 1701 "ticket":"cf3943767a35136252f69118b291b47006308e4215de41673ab118736e26605e", 1702 "votebit":"2", 1703 "signature":"1f8b3c8207fa67d91a65d8742e5026044ccebd6b4865579a1f75d6e9a40a56f9a96e091397d2ec9f8fca773c68e961b93fe380a694aceecfd8f9b972f1e4d59db9" 1704 }], 1705 "startvotereply": { 1706 "startblockheight":"282899", 1707 "startblockhash":"00000000017236b62ff1ce136328e6fb4bcd171801a281ce0a662e63cbc4c4fa", 1708 "endheight":"284915", 1709 "eligibletickets":[ 1710 "000011e329fe0359ea1d2070d927c93971232c1118502dddf0b7f1014bf38d97", 1711 "0004b0f8b2883a2150749b2c8ba05652b02220e98895999fd96df790384888f9", 1712 "00107166c5fc5c322ecda3748a1896f4a2de6672aae25014123d2cedc83e8f42", 1713 "002272cf4788c3f726c30472f9c97d2ce66b997b5762ff4df6a05c4761272413" 1714 ] 1715 } 1716 } 1717 ``` 1718 1719 ### `User Comments Likes` 1720 1721 Retrieve the comment votes for the current logged in user given a proposal token 1722 1723 **Route:** `GET v1/user/proposals/{token}/commentslikes` 1724 1725 **Params:** none 1726 1727 **Results:** 1728 1729 | | Type | Description | 1730 | - | - | - | 1731 | commentslikes | array of CommentLike | Likes issued by the current user | 1732 1733 **CommentLike:** 1734 1735 | | Type | Description | 1736 | - | - | - | 1737 | action | string | Up or downvote (1, -1) | 1738 | commentid | string | Comment ID | 1739 | token | string | Proposal censorship token | 1740 1741 **Example:** 1742 1743 Request: 1744 Path: `v1/user/proposals/8a11057fb910564a7d2506430505c3991f59e35f8a7757b8000a032505b254d8/commentslikes` 1745 1746 Reply: 1747 ```json 1748 { 1749 "commentslikes": 1750 [ 1751 { 1752 "action":"-1", 1753 "commentid":"1", 1754 "token":"8a11057fb910564a7d2506430505c3991f59e35f8a7757b8000a032505b254d8" 1755 }, 1756 { 1757 "action":"1", 1758 "commentid":"2", 1759 "token":"8a11057fb910564a7d2506430505c3991f59e35f8a7757b8000a032505b254d8" 1760 } 1761 ] 1762 } 1763 ``` 1764 1765 ### `Token inventory` 1766 1767 Retrieve the censorship record tokens of all proposals in the inventory. The 1768 tokens are categorized by stage of the voting process and sorted according to 1769 the rules listed below. Unvetted proposal tokens are only returned to admins. 1770 Unvetted proposals include unvreviewed and censored proposals. 1771 1772 Sorted by record timestamp in descending order: 1773 Pre, Abandonded, Unreviewed, Censored 1774 1775 Sorted by voting period end block height in descending order: 1776 Active, Approved, Rejected 1777 1778 **Route:** `GET v1/proposals/tokeninventory` 1779 1780 **Params:** none 1781 1782 **Results:** 1783 1784 | | Type | Description | 1785 | - | - | - | 1786 | pre | []string | Tokens of all vetted proposals that are pre-vote. | 1787 | active | []string | Tokens of all vetted proposals with an active voting period. | 1788 | approved | []string | Tokens of all vetted proposals that have been approved by a vote. | 1789 | rejected | []string | Tokens of all vetted proposals that have been rejected by a vote. | 1790 | abandoned | []string | Tokens of all vetted proposals that have been abandoned. | 1791 | unreviewed | []string | Tokens of all unreviewed proposals. | 1792 | censored | []string | Tokens of all censored proposals. | 1793 1794 **Example:** 1795 Request: 1796 Path: `v1/proposals/tokeninventory` 1797 1798 Reply: 1799 1800 ```json 1801 { 1802 "pre": [ 1803 "567ec4cdca78362f725dbb2b8b5161991fe6ba3bb6da1ad3f99067dd4712e48e" 1804 ], 1805 "active": [ 1806 "79cb792d8a15e83ce6809b2846f4dfdd04a65f5aa674c04926599fabf80c1b62" 1807 ], 1808 "approved": [], 1809 "rejected": [], 1810 "abandoned": [ 1811 "99376fbf7b79e30a7ff778743da46e04ae3b360109fa71011930f4c9a15c4ef5" 1812 ] 1813 } 1814 ``` 1815 1816 ### `Set TOTP` 1817 1818 This user route requests a new TOTP secret to be generated by the server. It 1819 will return a Key/Image pair that can be used to save the key pair to 1820 a TOTP app of the users' choice. 1821 1822 If the user already has a TOTP set, they must also add a code generated from 1823 the currently set secret. 1824 1825 For now we just have 'basic' TOTP added, but in the future we can add different 1826 or custom types of TOTP. 1827 1828 **Route:** `POST /v1/user/totp` 1829 1830 **Params:** 1831 1832 | Parameter | Type | Description | Required | 1833 |-|-|-|-| 1834 | type | TOTPMethodT | The type of TOTP that the user wants generated | Yes | 1835 | code | string | A code generated by | Yes | 1836 1837 **Results:** 1838 1839 | | Type | Description | 1840 | - | - | - | 1841 | key | string | The secret key generated by the server. | 1842 | image | string | The 'image' of the secret that can be used to generate a QRcode. | 1843 1844 On success the call shall return `200 OK`. 1845 1846 On failure the call shall return `400 Bad Request` and one of the following error codes: 1847 - [`ErrorStatusTOTPFailedValidation`](#ErrorStatusTOTPFailedValidation) 1848 - [`ErrorStatusInvalidInput`](#ErrorStatusInvalidInput) 1849 1850 **Example:** 1851 1852 Request: 1853 1854 ```json 1855 { 1856 "type":"1", 1857 "code":"994411" 1858 } 1859 ``` 1860 1861 Reply: 1862 1863 ```json 1864 { 1865 "key":"", 1866 "image":"", 1867 } 1868 ``` 1869 1870 ### `Verify TOTP` 1871 1872 This verify and confirms the user's previous requested new TOTP secret with a 1873 code generated by their TOTP app with the secret key provided from the SetTOTP 1874 request. 1875 1876 **Route:** `POST /v1/user/verifytotp` 1877 1878 **Params:** 1879 1880 | Parameter | Type | Description | Required | 1881 |-|-|-|-| 1882 | code | string | The TOTP code generate from the user's secret key. | Yes | 1883 1884 **Results:** none 1885 1886 On success the call shall return `200 OK`. 1887 1888 On failure the call shall return `400 Bad Request` and one of the following error codes: 1889 - [`ErrorStatusTOTPFailedValidation`](#ErrorStatusTOTPFailedValidation) 1890 1891 **Example:** 1892 1893 Request: 1894 1895 The request params should be provided within the URL: 1896 1897 ```json 1898 { 1899 "code":"994411" 1900 } 1901 ``` 1902 1903 Reply: 1904 1905 ```json 1906 {} 1907 ``` 1908 1909 ### `Error codes` 1910 1911 | Status | Value | Description | 1912 |-|-|-| 1913 | <a name="ErrorStatusInvalid">ErrorStatusInvalid</a> | 0 | The operation returned an invalid status. This shall be considered a bug. | 1914 | <a name="ErrorStatusInvalidPassword">ErrorStatusInvalidPassword</a> | 1 | The password is invalid. | 1915 | <a name="ErrorStatusMalformedEmail">ErrorStatusMalformedEmail</a> | 2 | The provided email address was malformed. | 1916 | <a name="ErrorStatusVerificationTokenInvalid">ErrorStatusVerificationTokenInvalid</a> | 3 | The provided user activation token is invalid. | 1917 | <a name="ErrorStatusVerificationTokenExpired">ErrorStatusVerificationTokenExpired</a> | 4 | The provided user activation token is expired. | 1918 | <a name="ErrorStatusProposalMissingFiles">ErrorStatusProposalMissingFiles</a> | 5 | The provided proposal does not have files. This error may include additional context: index file is missing - "index.md". | 1919 | <a name="ErrorStatusProposalNotFound">ErrorStatusProposalNotFound</a> | 6 | The requested proposal does not exist. | 1920 | <a name="ErrorStatusProposalDuplicateFilenames">ErrorStatusProposalDuplicateFilenames</a> | 7 | The provided proposal has duplicate files. This error is provided with additional context: the duplicate name(s). | 1921 | <a name="ErrorStatusProposalInvalidTitle">ErrorStatusProposalInvalidTitle</a> | 8 | The provided proposal title is invalid. This error is provided with additional context: the regular expression accepted. | 1922 | <a name="ErrorStatusMaxMDsExceededPolicy">ErrorStatusMaxMDsExceededPolicy</a> | 9 | The submitted proposal has too many markdown files. Limits can be obtained by issuing the [Policy](#policy) command. | 1923 | <a name="ErrorStatusMaxImagesExceededPolicy">ErrorStatusMaxImagesExceededPolicy</a> | 10 | The submitted proposal has too many images. Limits can be obtained by issuing the [Policy](#policy) command. | 1924 | <a name="ErrorStatusMaxMDSizeExceededPolicy">ErrorStatusMaxMDSizeExceededPolicy</a> | 11 | The submitted proposal markdown is too large. Limits can be obtained by issuing the [Policy](#policy) command. | 1925 | <a name="ErrorStatusMaxImageSizeExceededPolicy">ErrorStatusMaxImageSizeExceededPolicy</a> | 12 | The submitted proposal has one or more images that are too large. Limits can be obtained by issuing the [Policy](#policy) command. | 1926 | <a name="ErrorStatusMalformedPassword">ErrorStatusMalformedPassword</a> | 13 | The provided password was malformed. | 1927 | <a name="ErrorStatusCommentNotFound">ErrorStatusCommentNotFound</a> | 14 | The requested comment does not exist. | 1928 | <a name="ErrorStatusInvalidFilename">ErrorStatusInvalidFilename</a> | 15 | The filename was invalid. | 1929 | <a name="ErrorStatusInvalidFileDigest">ErrorStatusInvalidFileDigest</a> | 16 | The digest (SHA-256 checksum) provided for one of the proposal files was incorrect. This error is provided with additional context: The name of the file with the invalid digest. | 1930 | <a name="ErrorStatusInvalidBase64">ErrorStatusInvalidBase64</a> | 17 | The name of the file with the invalid encoding.The Base64 encoding provided for one of the proposal files was incorrect. This error is provided with additional context: the name of the file with the invalid encoding. | 1931 | <a name="ErrorStatusInvalidMIMEType">ErrorStatusInvalidMIMEType</a> | 18 | The MIME type provided for one of the proposal files was not the same as the one derived from the file's content. This error is provided with additional context: The name of the file with the invalid MIME type and the MIME type detected for the file's content. | 1932 | <a name="ErrorStatusUnsupportedMIMEType">ErrorStatusUnsupportedMIMEType</a> | 19 | The MIME type provided for one of the proposal files is not supported. This error is provided with additional context: The name of the file with the unsupported MIME type and the MIME type that is unsupported. | 1933 | <a name="ErrorStatusInvalidPropStatusTransition">ErrorStatusInvalidPropStatusTransition</a> | 20 | The provided proposal cannot be changed to the given status. | 1934 | <a name="ErrorStatusInvalidPublicKey">ErrorStatusInvalidPublicKey</a> | 21 | Invalid public key. | 1935 | <a name="ErrorStatusNoPublicKey">ErrorStatusNoPublicKey</a> | 22 | User does not have an active public key. | 1936 | <a name="ErrorStatusInvalidSignature">ErrorStatusInvalidSignature</a> | 23 | Invalid signature. | 1937 | <a name="ErrorStatusInvalidInput">ErrorStatusInvalidInput</a> | 24 | Invalid input. | 1938 | <a name="ErrorStatusInvalidSigningKey">ErrorStatusInvalidSigningKey</a> | 25 | Invalid signing key. | 1939 | <a name="ErrorStatusCommentLengthExceededPolicy">ErrorStatusCommentLengthExceededPolicy</a> | 26 | The submitted comment length is too large. | 1940 | <a name="ErrorStatusUserNotFound">ErrorStatusUserNotFound</a> | 27 | The user was not found. | 1941 | <a name="ErrorStatusWrongStatus">ErrorStatusWrongStatus</a> | 28 | The proposal has the wrong status. | 1942 | <a name="ErrorStatusNotLoggedIn">ErrorStatusNotLoggedIn</a> | 29 | The user must be logged in for this action. | 1943 | <a name="ErrorStatusUserNotPaid">ErrorStatusUserNotPaid</a> | 30 | The user hasn't paid the registration fee. | 1944 | <a name="ErrorStatusReviewerAdminEqualsAuthor">ErrorStatusReviewerAdminEqualsAuthor</a> | 31 | The user cannot change the status of his own proposal. | 1945 | <a name="ErrorStatusMalformedUsername">ErrorStatusMalformedUsername</a> | 32 | The provided username was malformed. | 1946 | <a name="ErrorStatusDuplicateUsername">ErrorStatusDuplicateUsername</a> | 33 | The provided username is already taken by another user. | 1947 | <a name="ErrorStatusVerificationTokenUnexpired">ErrorStatusVerificationTokenUnexpired</a> | 34 | A verification token has already been generated and hasn't expired yet. | 1948 | <a name="ErrorStatusCannotVerifyPayment">ErrorStatusCannotVerifyPayment</a> | 35 | The server cannot verify the payment at this time, please try again later. | 1949 | <a name="ErrorStatusDuplicatePublicKey">ErrorStatusDuplicatePublicKey</a> | 36 | The public key provided is already taken by another user. | 1950 | <a name="ErrorStatusInvalidPropVoteStatus">ErrorStatusInvalidPropVoteStatus</a> | 37 | Invalid proposal vote status. | 1951 | <a name="ErrorStatusUserLocked">ErrorStatusUserLocked</a> | 38 | User locked due to too many login attempts. | 1952 | <a name="ErrorStatusNoProposalCredits">ErrorStatusNoProposalCredits</a> | 39 | No proposal credits. | 1953 | <a name="ErrorStatusInvalidUserManageAction">ErrorStatusInvalidUserManageAction</a> | 40 | Invalid action for editing a user. | 1954 | <a name="ErrorStatusUserActionNotAllowed">ErrorStatusUserActionNotAllowed</a> | 41 | User action is not allowed. | 1955 | <a name="ErrorStatusWrongVoteStatus">ErrorStatusWrongVoteStatus</a> | 42 | The proposal has the wrong vote status for the action to be performed. | 1956 | <a name="ErrorStatusCannotVoteOnPropComment">ErrorStatusCannotVoteOnPropComment</a> | 44 | Cannot vote on proposal comment. | 1957 | <a name="ErrorStatusChangeMessageCannotBeBlank">ErrorStatusChangeMessageCannotBeBlank</a> | 45 | Status change message cannot be blank. | 1958 | <a name="ErrorStatusCensorReasonCannotBeBlank">ErrorStatusCensorReasonCannotBeBlank</a> | 46 | Censor comment reason cannot be blank. | 1959 | <a name="ErrorStatusCannotCensorComment">ErrorStatusCannotCensorComment</a> | 47 | Cannot censor comment. | 1960 | <a name="ErrorStatusUserNotAuthor">ErrorStatusUserNotAuthor</a> | 48 | User is not the proposal author. | 1961 | <a name="ErrorStatusVoteNotAuthorized">ErrorStatusVoteNotAuthorized</a> | 49 | Vote has not been authorized. | 1962 | <a name="ErrorStatusVoteAlreadyAuthorized">ErrorStatusVoteAlreadyAuthorized</a> | 50 | Vote has already been authorized. | 1963 | <a name="ErrorStatusInvalidAuthVoteAction">ErrorStatusInvalidAuthVoteAction</a> | 51 | Invalid authorize vote action. | 1964 | <a name="ErrorStatusUserDeactivated">ErrorStatusUserDeactivated</a> | 52 | Cannot login because user account is deactivated. | 1965 | <a name="ErrorStatusInvalidPropVoteBits">ErrorStatusInvalidPropVoteBits</a> | 53 | Invalid proposal vote option bits. | 1966 | <a name="ErrorStatusInvalidPropVoteParams">ErrorStatusInvalidPropVoteParams</a> | 54 | Invalid proposal vote parameters. | 1967 | <a name="ErrorStatusEmailNotVerified">ErrorStatusEmailNotVerified</a> | 55 | Cannot login because user's email is not yet verified. | 1968 | <a name="ErrorStatusInvalidUUID">ErrorStatusInvalidUUID</a> | 56 | Invalid user UUID. | 1969 | <a name="ErrorStatusInvalidLikeCommentAction">ErrorStatusInvalidLikeCommentAction</a> | 57 | Invalid like comment action. | 1970 | <a name="ErrorStatusInvalidCensorshipToken">ErrorStatusInvalidCensorshipToken</a> | 58 | Invalid proposal censorship token. | 1971 | <a name="ErrorStatusEmailAlreadyVerified">ErrorStatusEmailAlreadyVerified</a> | 59 | Email address is already verified. | 1972 | <a name="ErrorStatusNoProposalChanges">ErrorStatusNoProposalChanges</a> | 60 | No changes found in proposal. | 1973 | <a name="ErrorStatusMaxProposalsExceedsPolicy">ErrorStatusMaxProposalsExceededPolicy</a> | 61 | Number of proposals requested exceeded the ProposalListPageSize. | 1974 | <a name="ErrorStatusDuplicateComment">ErrorStatusDuplicateComment</a> | 62 | Duplicate comment. | 1975 | <a name="ErrorStatusInvalidLogin">ErrorStatusInvalidLogin</a> | 63 | Invalid login credentials. | 1976 | <a name="ErrorStatusCommentIsCensored">ErrorStatusCommentIsCensored</a> | 64 | Comment is censored. | 1977 | <a name="ErrorStatusInvalidProposalVersion">ErrorStatusInvalidProposalVersion</a> | 65 | Invalid proposal version. | 1978 | <a name="ErrorStatusMetadataInvalid">ErrorStatusMetadataInvalid</a> | 66 | Invalid proposal metadata. | 1979 | <a name="ErrorStatusMetadataMissing">ErrorStatusMetadataMissing</a> | 67 | Missing proposal metadata. | 1980 | <a name="ErrorStatusMetadataDigestInvalid">ErrorStatusMetadataDigestInvalid</a> | 68 | Proposal metadata digest invalid. | 1981 | <a name="ErrorStatusInvalidVoteType">ErrorStatusInvalidVoteType</a> | 69 | Invalid vote type. | 1982 | <a name="ErrorStatusInvalidVoteOptions">ErrorStatusInvalidVoteOptions</a> | 70 | Invalid vote option. | 1983 | <a name="ErrorStatusLinkByDeadlineNotMet">ErrorStatusLinkByDeadlineNotMet</a> | 71 | Linkby not met yet. | 1984 | <a name="ErrorStatusNoLinkedProposals">ErrorStatusNoLinkedProposals</a> | 72 | No linked proposals. | 1985 | <a name="ErrorStatusInvalidLinkTo">ErrorStatusInvalidLinkTo</a> | 73 | Invalid propsoal linkto. | 1986 | <a name="ErrorStatusInvalidLinkBy">ErrorStatusInvalidLinkBy</a> | 74 | Invalid proposal linkby. | 1987 | <a name="ErrorStatusInvalidRunoffVote">ErrorStatusInvalidRunoffVote</a> | 75 | Invalid runoff vote. | 1988 | <a name="ErrorStatusWrongProposalType">ErrorStatusWrongProposalType</a> | 76 | Wrong proposal type. | 1989 | <a name="ErrorStatusTOTPFailedValidation">ErrorStatusTOTPFailedValidation</a> | 77 | TOTP code provided doesn't failed validation with current key. | 1990 | <a name="ErrorStatusTOTPInvalidType">ErrorStatusTOTPInvalidType</a> | 78 | Invalid TOTP Type. | 1991 | <a name="ErrorStatusRequiresTOTPCode">ErrorStatusRequiresTOTPCode</a> | 79 | User has verified TOTP secret and login requires code. | 1992 | <a name="ErrorStatusTOTPWaitForNewCode">ErrorStatusTOTPWaitForNewCode</a> | 80 | Must wait until next TOTP code window before another login attempt. | 1993 1994 1995 ### `Proposal status codes` 1996 1997 | Status | Value | Description | 1998 |-|-|-| 1999 | <a name="PropStatusInvalid">PropStatusInvalid</a>| 0 | An invalid status. This shall be considered a bug. | 2000 | <a name="PropStatusNotFound">PropStatusNotFound</a> | 1 | The proposal was not found. | 2001 | <a name="PropStatusNotReviewed">PropStatusNotReviewed</a> | 2 | The proposal has not been reviewed by an admin. | 2002 | <a name="PropStatusCensored">PropStatusCensored</a> | 3 | The proposal has been censored by an admin. | 2003 | <a name="PropStatusPublic">PropStatusPublic</a> | 4 | The proposal has been published by an admin. | 2004 | <a name="PropStatusUnreviewedChanges">PropStatusUnreviewedChanges</a> | 5 | The proposal has not been rewieved by an admin yet and has been edited by the author. | 2005 | <a name="PropStatusAbandoned">PropStatusAbandoned</a> | 6 | The proposal is public and has been deemed abandoned by an admin. | 2006 2007 ### `Vote types` 2008 | Status | Value | Description | 2009 |-|-|-| 2010 | <a name="VoteTypeInvalid">VoteTypeInvalid</a>| 0 | An invalid vote type. This shall be considered a bug. | 2011 | <a name="VoteTypeStandard">VoteTypeStandard</a>| 1 | A simple approve or reject proposal vote where the winner is the voting option that has met the specified pass and quorum requirements. | 2012 | <a name="VoteTypeRunoff">VoteType</a>| 2 | A runoff vote that multiple proposals compete in. All proposals are voted on like normal, but there can only be one winner in a runoff vote. The winner is the proposal that meets the quorum requirement, meets the pass requirement, and that has the most net yes votes. The winning proposal is considered approved and all other proposals are considered rejected. If no proposals meet the quorum and pass requirements then all proposals are considered rejected. Note: in a runoff vote it is possible for a proposal to meet the quorum and pass requirements but still be rejected if it does not have the most net yes votes. | 2013 2014 ### `User edit actions` 2015 2016 | Status | Value | Description | 2017 |-|-|-| 2018 | <a name="UserManageInvalid">UserManageInvalid</a>| 0 | An invalid action. This shall be considered a bug. | 2019 | <a name="UserManageExpireNewUserVerification">UserManageExpireNewUserVerification</a> | 1 | Expires the new user verification token. | 2020 | <a name="UserManageExpireUpdateKeyVerification">UserManageExpireUpdateKeyVerification</a> | 2 | Expires the update key verification token. | 2021 | <a name="UserManageExpireResetPasswordVerification">UserManageExpireResetPasswordVerification</a> | 3 | Expires the reset password verification token. | 2022 | <a name="UserManageClearUserPaywall">UserManageClearUserPaywall</a> | 4 | Clears the user's paywall. | 2023 | <a name="UserManageUnlock">UserManageUnlock</a> | 5 | Unlocks a user's account. | 2024 | <a name="UserManageDeactivate">UserManageDeactivate</a> | 6 | Deactivates a user's account so that they are unable to login. | 2025 | <a name="UserManageReactivate">UserManageReactivate</a> | 7 | Reactivates a user's account. | 2026 2027 ### `User` 2028 2029 | | Type | Description | 2030 |-|-|-| 2031 | id | string | The unique id of the user. | 2032 | email | string | Email address. | 2033 | username | string | Unique username. | 2034 | isadmin | boolean | Whether the user is an admin or not. | 2035 | newuserpaywalladdress | string | The address in which to send the transaction containing the `newuserpaywallamount`. If the user has already paid, this field will be empty or not present. | 2036 | newuserpaywallamount | int64 | The amount of DCR (in atoms) to send to `newuserpaywalladdress`. If the user has already paid, this field will be empty or not present. | 2037 | newuserpaywalltxnotbefore | int64 | The minimum UNIX time (in seconds) required for the block containing the transaction sent to `newuserpaywalladdress`. If the user has already paid, this field will be empty or not present. | 2038 | newuserpaywalltx | string | The transaction used to pay the `newuserpaywallamount` at `newuserpaywalladdress`. | 2039 | newuserpaywallpollexpiry | int64 | The UNIX time (in seconds) for when the server will stop polling the server for transactions at `newuserpaywalladdress`. | 2040 | newuserverificationtoken | string | The verification token which is sent to the user's email address. | 2041 | newuserverificationexpiry | int64 | The UNIX time (in seconds) for when the `newuserverificationtoken` expires. | 2042 | updatekeyverificationtoken | string | The verification token which is sent to the user's email address. | 2043 | updatekeyverificationexpiry | int64 | The UNIX time (in seconds) for when the `updatekeyverificationtoken` expires. | 2044 | resetpasswordverificationtoken | string | The verification token which is sent to the user's email address. | 2045 | resetpasswordverificationexpiry | int64 | The UNIX time (in seconds) for when the `resetpasswordverificationtoken` expires. | 2046 | lastlogintime | int64 | The UNIX timestamp of the last login date; it will be 0 if the user has not logged in before. | 2047 | failedloginattempts | uint64 | The number of consecutive failed login attempts. | 2048 | islocked | boolean | Whether the user account is locked due to too many failed login attempts. | 2049 | isdeactivated | boolean | Whether the user account is deactivated. Deactivated accounts cannot login. | 2050 | identities | array of [`Identity`](#identity)s | Identities, both activated and deactivated, of the user. | 2051 | proposalcredits | uint64 | The number of available proposal credits the user has. | 2052 | emailnotifications | uint64 | A flag storing the user's preferences for email notifications. Individual notification preferences are stored in bits of the number, and are [documented below](#emailnotifications). | 2053 2054 ### `Email notifications` 2055 2056 These are the available email notifications that can be sent. 2057 2058 | Description | Value | 2059 |-|-| 2060 | **For my proposals** | 2061 | Proposal status change (approved/censored) | `1 << 0` | 2062 | Proposal vote started | `1 << 1` | 2063 | **For others' proposals** | 2064 | New proposal published | `1 << 2` | 2065 | Proposal edited | `1 << 3` | 2066 | Proposal vote started | `1 << 4` | 2067 | **Admins for others' proposals** | 2068 | Proposal submitted for review | `1 << 5` | 2069 | Proposal vote authorized | `1 << 6` | 2070 2071 ### `Abridged User` 2072 2073 This is a shortened representation of a user, used for lists. 2074 2075 | | Type | Description | 2076 |-|-|-| 2077 | id | string | The unique id of the user. | 2078 | email | string | Email address. | 2079 | username | string | Unique username. | 2080 2081 ### `Proposal` 2082 2083 | | Type | Description | 2084 |-|-|-| 2085 | name | string | The name of the proposal. | 2086 | state | number | Current state of the proposal. | 2087 | status | number | Current status of the proposal. | 2088 | timestamp | number | The unix time of the last update of the proposal. | 2089 | userid | string | The ID of the user who created the proposal. | 2090 | username | string | Proposal author's username. | 2091 | publickey | string | The public key of the user who created the proposal. | 2092 | signature | string | The signature of the merkle root, signed by the user who created the proposal. | 2093 | numcomments | number | The number of comments on the proposal. This should be ignored for proposals which are not public. | 2094 | version | string | The proposal version. | 2095 | statatuschangemessage | string | Message associated to the status change (omitempty). | 2096 | pubishedat | number | The timestamp of when the proposal was published (omitempty). | 2097 | censoredat | number | The timestamp of when the proposal was censored (omitempty). | 2098 | abandonedat | The timestamp of when the proposal was abandoned (omitempty). | 2099 | linkto | string | Censorship token of proposal to link to (omitempty). | 2100 | linkby | number | Unix timestamp of RFP link by deadline (omitempty). | 2101 | files | [][`File`](#file)s | Proposal files. This property will only be populated for the [`Proposal details`](#proposal-details) call. | 2102 | metadata | [][`Metadata`](#metadata) | Proposal metadata. This will contain a [`ProposalMetadata`](#proposal-metadata). | 2103 | censorshiprecord | [`CensorshipRecord`](#censorship-record) | The censorship record that was created when the proposal was submitted. | 2104 2105 ### `Identity` 2106 2107 | | Type | Description | 2108 |-|-|-| 2109 | pubkey | string | The user's public key. | 2110 | isactive | boolean | Whether or not the identity is active. | 2111 2112 ### `File` 2113 2114 | | Type | Description | 2115 |-|-|-| 2116 | name | string | Name is the suggested filename. There should be no filenames that are overlapping and the name shall be validated before being used. | 2117 | mime | string | MIME type of the payload. Currently the system only supports md and png files. The server shall reject invalid MIME types. | 2118 | digest | string | Digest is a SHA256 digest of the payload. The digest shall be verified by politeiad. | 2119 | payload | string | Payload is the actual file content. It shall be base64 encoded. Files have size limits that can be obtained via the [`Policy`](#policy) call. The server shall strictly enforce policy limits. | 2120 2121 ### `Metadata` 2122 2123 | | Type | Description | 2124 |-|-|-| 2125 | Digest | string | SHA256 digest of the JSON encoded payload | 2126 | Hint | string | Hint that describes the payload | 2127 | Payload | string | Base64 encoded metadata content where the metadata content is JSON encoded. | 2128 2129 ### `Proposal metadata` 2130 | | Type | Description | 2131 |-|-|-| 2132 | Name | string | Proposal name. | 2133 | LinkTo | string | Censorship token of the proposal to link to (optional). | 2134 | LinkBy | int64 | Unix timestamp of the RFP deadline (optional). | 2135 2136 ### `Vote summary` 2137 2138 | | Type | Description | 2139 |-|-|-| 2140 | status | int | Status identifier | 2141 | approved | bool | Has the proposal vote passed | 2142 | type | [`VoteT`](#vote-types)| Vote type (omitempty) | 2143 | eligibletickets | int | Total number of eligible tickets (omitempty) | 2144 | duration | uint32 | Duration of the vote in blocks (omitempty) | 2145 | endheight | uint64 | The chain height in which the vote will end (omitempty) | 2146 | quorumpercentage | uint32 | Percent of eligible votes required for quorum (omitempty) | 2147 | passpercentage | uint32 | Percent of total votes required to pass (omitempty) | 2148 | optionsresult | array of VoteOptionResult | Option description along with the number of votes it has received (omitempty) | 2149 2150 ### `Censorship record` 2151 2152 | | Type | Description | 2153 |-|-|-| 2154 | token | string | The token is a 32 byte random number that was assigned to identify the submitted proposal. This is the key to later retrieve the submitted proposal from the system. | 2155 | merkle | string | Merkle root of the proposal. This is defined as the sorted digests of all files proposal files. The client should cross verify this value. | 2156 | signature | string | Signature of byte array representations of merkle+token. The token byte array is appended to the merkle root byte array and then signed. The client should verify the signature. | 2157 2158 ### `Login reply` 2159 2160 This object will be sent in the result body on a successful [`Login`](#login) 2161 or [`Me`](#me) call. 2162 2163 | Parameter | Type | Description | 2164 |-|-|-| 2165 | isadmin | boolean | This indicates if the user has publish/censor privileges. | 2166 | userid | string | Unique user identifier. | 2167 | email | string | Current user email address. | 2168 | publickey | string | Current public key. | 2169 | paywalladdress | String | The address in which to send the transaction containing the `paywallamount`. If the user has already paid, this field will be empty or not present. | 2170 | paywallamount | Int64 | The amount of DCR (in atoms) to send to `paywalladdress`. If the user has already paid, this field will be empty or not present. | 2171 | paywalltxnotbefore | Int64 | The minimum UNIX time (in seconds) required for the block containing the transaction sent to `paywalladdress`. If the user has already paid, this field will be empty or not present. | 2172 | lastlogintime | int64 | The UNIX timestamp of the last login date; it will be 0 if the user has not logged in before. | 2173 | sessionmaxage | int64 | The UNIX timestamp of the session max age. | 2174 2175 ### `Proposal credit` 2176 A proposal credit allows the user to submit a new proposal. Proposal credits are a spam prevention measure. Credits are created when a user sends a payment to a proposal paywall. The user can request proposal paywall details using the [`Proposal paywall details`](#proposal-paywall-details) endpoint. A credit is automatically spent every time a user submits a new proposal. 2177 2178 | | Type | Description | 2179 |-|-|-| 2180 | paywallid | uint64 | The ID of the proposal paywall that created this credit. | 2181 | price | uint64 | The price that the credit was purchased at in atoms. | 2182 | datepurchased | int64 | A Unix timestamp of the purchase data. | 2183 | txid | string | The txID of the Decred transaction that paid for this credit. | 2184 2185 ## Websocket methods 2186 2187 ### `WSHeader` 2188 | Parameter | Type | Description | Required | 2189 |-|-|-|-| 2190 |Command|string|Type of JSON structure that follows the header|yes| 2191 |ID|string|Client settable ID|no| 2192 2193 **WSHeader** is required as a prefix to every other command on both the client 2194 and server side. 2195 2196 ### `WSError` 2197 | Parameter | Type | Description | Required | 2198 |-|-|-|-| 2199 |Command|string|Type of JSON structure that follows the header|no| 2200 |ID|string|Client settable ID|no| 2201 |Errors|array of string|All errors that occurred during execution of the failed command|yes| 2202 2203 **WSError** always flows from server to client. 2204 2205 **example** 2206 ``` 2207 { 2208 "command": "error", 2209 "id": "1" 2210 } 2211 { 2212 "command": "subscribe", 2213 "id": "1", 2214 "errors": [ 2215 "invalid subscription pingo" 2216 ] 2217 } 2218 ``` 2219 2220 ### `WSSubscribe` 2221 | Parameter | Type | Description | Required | 2222 |-|-|-|-| 2223 |RPCS|array of string|Subscriptions|yes| 2224 2225 Current valid subscriptions are `ping`. 2226 2227 Sending additional `subscribe` commands will result in the old subscription 2228 list being overwritten and thus an empty `rpcs` cancels all subscriptions. 2229 2230 **WSSubscribe** always flows from client to server. 2231 2232 **Example** 2233 ``` 2234 { 2235 "command": "subscribe", 2236 "id": "1" 2237 } 2238 { 2239 "rpcs": [ 2240 "ping" 2241 ] 2242 } 2243 ``` 2244 2245 2246 ### `WSPing` 2247 | Parameter | Type | Description | Required | 2248 |-|-|-|-| 2249 |Timestamp|int64|Server timestamp|yes| 2250 2251 **WSPing** always flows from server to client. 2252 2253 **example** 2254 ``` 2255 { 2256 "command": "ping" 2257 } 2258 { 2259 "timestamp": 1547653596 2260 }