API documentation

General

We have a standard REST API for managing secrets. All requests and responses use the JSON format.

You can use the API to handle both encryption and decryption of the secret outside of our service, without needing to load any assets from us. It also allows you to use any kind of delivery method for the public password part (half of the password which is used to derive the encryption key for the secret from) and basically create any kind of custom configuration for viewing the secret.

Code examples

Please see our API examples on how to use the API.

Authentication

We use two kinds of API keys for authentication:

  • Public API keys
  • Private API keys

Public API keys are meant for creating a custom self-hosted view secret page. All other API actions require a private API key.

Public API keys can be used in public scripts. Private API keys must always be kept private.

Both API keys contain the type of the key in the key itself so they can be easily distinguished.

The API key must be sent in the Authorization: ApiKey <key> header, like:

Authorization: ApiKey public_key_abc...

Errors

In case of unsuccessful query, the response will have an error object.

The object contains the following fields:

  • message: the full error message, e.g. "Ciphertext can't be blank"
  • field: the field the error is about, e.g. "ciphertext"

The HTTP response status code will also indicate an error, for example:

  • 403 Forbidden: the request was not allowed, for example because of invalid API key
  • 404 Not Found: the requested secret was not found

View secret

Request type: GET

Request path: https://password.link/api/secrets/<id>

Status code of successful query: 200 OK


Fetch the ciphertext, private password part and other details of a secret. Requires the public API key.

This API query can be used to create a custom self-hosted view secret page which does not load any scripts or other assets from our service.

Successfully fetching a secret automatically marks it as viewed and so deletes the ciphertext and private password part from our database, making it impossible to view the secret again.

Required parameters
  • id: the id of the secret, for example dT5g

Query example

curl -H "Authorization: ApiKey public_key_abcd" https://password.link/api/secrets/dT5g

Response example

{
  "data": {
    "id": "the ID of the secret",
    "ciphertext": "the ciphertext of the secret",
    "password_part_private": "the private password part of the secret",
    "message": "the message for the secret, if set"
  },
  "metadata": {
    "secrets_total": 1,
    "secrets_usage": 1,
    "secrets_allowance": 1
  }
}

Code example

Please see our API examples.

Store secret

Request type: POST

Request path: https://password.link/api/secrets

Status code of successful query: 201 Created


Store an encrypted secret.

Encrypting the secret client-side before sending it to our API requires creating an SJCL compatible ciphertext JSON string. Please refer to the SJCL documentation for proper format of the JSON string, and see our API examples for examples on how to create the ciphertext.

Creating the password for encrypting the secret

The password which is used to encrypt the secret in AES must consist of two 18 character long strings. The actual encryption key will be the concatenation of these two strings ("private part" + "public part") and is derived from the password by SJCL using PBKDF2.

Example of creating the private and public password parts:

Password: fkgjbnvlakwiejgutnFIGKEOTIRUBNAKQJRL
=> Private part: fkgjbnvlakwiejgutn
=> Public part: FIGKEOTIRUBNAKQJRL

The private part needs to be Base64 encoded before sending over to our API. If you use the view secret page provided by us, you must also Base64 encode the public part. If you use a self-hosted view secret page, you can use the same method or create your own. Please see our API examples for reference implementations.

Required SJCL AES settings

You must use the following settings for AES when encrypting the secret:

  • Mode: AES-GCM (SJCL setting: "mode:gcm")
  • Key size: 256 bits (SJCL setting: "ks:256")
  • Key derivation function: PBKDF2 (SJCL default)
  • PBKDF2 iterations: 10000 (SJCL setting: "iter:10000")

Request payload

{
  "ciphertext": "ciphertext",
  "password_part_private": "password_part_private",
  "description": "description",
  "message": "message",
  "expiration": "expiration",
  "view_button": "view_button"
}

Request parameters

  • ciphertext: An SJCL compatible ciphertext JSON string of the secret, encoded in Base64.
  • password_part_private: the private password part which was used to encrypt the secret, in Base64 format. Must be 18 characters, supported characters: a-zA-Z0-9...
  • description (optional): a description for the secret. Cannot be seen when viewing the secret.
  • message (optional): a message for the secret. Will be shown along the secret.
  • expiration (optional): an expiration time for the secret, in hours. Possible values: 0-350.
  • view_button (optional): show a view secret button instead of showing the secret immediately after opening the link. To enable this feature set this to true.

Code example

Please see our API examples.

List secrets

Request type: GET

Request path: https://password.link/api/secrets

Status code of successful query: 200 OK


Fetch a list of secrets. Does not contain the ciphertexts private password parts, only IDs, descriptions and the like.

Limit and offset

The maximum amount of returned secrets for each query is limited to 50. You can control the offset with the optional offset query parameter.

For example to skip the first 50 records, use a query like: .../api/secrets?offset=50

Query example

curl -H "Authorization: ApiKey private_key_abcd" https://password.link/api/secrets

Response example

{
  "data": [
    {
      "id": the ID of the secret,
      "created_at": when the secret was stored,
      "message": the message for the secret,
      "description": the description of the secret,
      "view_button": is the view secret button enabled,
      "expiration": expiration time in hours,
      "expired": has the secret expired,
      "viewed_at": a timestamp of when the secret was viewed,
      "viewed_by_ip": IP address of the viewer,
      "viewed_by_user_agent": user agent of the viewer,
    }
  ],
  "metadata": {
    "secrets_total": 1,
    "secrets_usage": 1,
    "secrets_allowance": 1
  }
}

Delete secret

Request type: DELETE

Request path: https://password.link/api/secrets/<id>

Status code of successful query: 200 OK


Delete a stored secret.

Query example

curl -H "Authorization: ApiKey private_key_abcd" \
-X DELETE https://password.link/api/secrets/dT5g

Response example

{
  "data": null,
  "metadata": {
    "secrets_total": 1,
    "secrets_usage": 1,
    "secrets_allowance": 1
  }
}