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.
Create Secret
Request type: POST
Request path: https://password.link/api/secrets
Status code of successful query: 201 Created
This API call also supports adding an encrypted file attachment to the secret.
Create 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 <a href=\"https://github.com/bitwiseshiftleft/sjcl\">SJCL documentation</a> for proper format of the JSON string, and see our <a href=\"/p/docs/api/examples\">API examples</a> 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 <a href=\"/p/docs/api/examples\">API examples</a> 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",
"captcha": "captcha",
"password": "password",
"max_views": "max_views",
"attachment": {
"file_name": "file_name",
"file_type": "file_type",
"file_size": "file_size"
}
}
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.
- 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.
- captcha (optional): show a simple CAPTCHA before showing the secret, mainly for blocking automated scanners. To enable this feature set this to true.
- password (optional): a password for the secret.
- max_views (optional): how many times the secret can be viewed. Possible values: 1-100.
- attachment (optional): metadata for one encrypted file attachment. Requires an active plan. The encrypted file is uploaded separately after the secret has been created.
- attachment.file_name (required when attachment is provided): the original file name shown to the recipient.
- attachment.file_type (required when attachment is provided): the original MIME type, for example text/plain or application/pdf.
- attachment.file_size (required when attachment is provided): the original unencrypted file size in bytes.
Uploading an attachment
Uploading an attachment is a two-step process: first create the secret with attachment metadata, then encrypt and upload the encrypted attachment payload to the returned upload URL.
The secret contents and attachment contents are encrypted separately. The secret ciphertext is SJCL-compatible AES-GCM data, Base64 encoded. The attachment is encrypted with Web Crypto compatible AES-GCM data, then Base64 encoded.
The attachment encryption password is the raw private password part concatenated with the raw public password part: <raw private password part> + <raw public password part>.
When an attachment is included, the create secret response contains an upload target in data.attachment.upload:
{
"data": {
"id": "dT5g",
"attachment": {
"file_name": "example.txt",
"upload": {
"url": "/api/secrets/dT5g/attachment",
"fields": {},
"metadata": {}
}
}
},
"metadata": {}
}
Password.link-hosted upload URLs include an empty fields object. Direct storage upload URLs, such as pre-signed S3 URLs, return signing fields in metadata instead.
Before encryption, represent the file as a data URL: data:<mime-type>;base64,<base64 file bytes>.
Encrypt that data URL string with AES-GCM using PBKDF2-SHA256, 10000 iterations, a 256-bit derived key, a 16-byte random salt and a 12-byte random IV. Store the encrypted attachment payload as a JSON object containing cipher, iv and salt, then Base64 encode the JSON string.
{
"cipher": "binary string",
"iv": "binary string",
"salt": "binary string"
}
Upload the Base64 encoded encrypted JSON as text/plain using multipart/form-data to the returned upload.url. Add any returned upload.metadata or upload.fields key/value pairs to the form data before adding the encrypted file. The encrypted file field must be named file.
For Password.link upload URLs, include the same private API key in the Authorization: ApiKey <key> header. Direct storage upload URLs, such as pre-signed S3 URLs, should only receive the returned form fields and the encrypted file field.
The API create-secret response returns one attachment upload target for the single attachment parameter. Attachment size limits depend on the account or team allowance, and the encrypted upload is larger than the original file because the file is converted to Base64 and encrypted.
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 created,
"message": the message for the secret,
"description": the description of the secret,
"view_button": is the view secret button enabled,
"captcha": is the CAPTCHA enabled,
"password": does the secret have a password,
"expiration": expiration time in hours,
"expired": has the secret expired,
"view_times": how many times the secret has beew viewed,
"max_views": the maximum amount of times the secret can be viewed,
"views": [
{
"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 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
}
}
Create Secret Request
Request type: POST
Request path: https://password.link/api/secret_requests
Status code of successful query: 201 Created
Create a new Secret Request.
Request parameters
- description: description for the Secret Request.
- message: message for the Secret Request viewer.
- expiration: expiration time for the Secret Request, in hours.
- limit: usage limit for the Secret Request.
- send_request_to_email: send the created Secret Request link to the given email address.
- send_to_email: send the Secret link created using the Secret Request to the given email address.
- secret_description: description for the Secret created using the Secret Request.
- secret_message: message for the Secret created using the Secret Request.
- secret_expiration: expiration time for the Secret created using the Secret Request, in hours.
- secret_password: password for the Secret created using the Secret Request, in hours.
- secret_max_views: view limit for the Secret created using the Secret Request.
Query example
curl -H "Authorization: ApiKey private_key_abcd" \
-H "Content-Type: application/json" -X POST https://password.link/api/secret_requests
Response example
{
"data": {
"id":"5e976e6e-d205-4f58-8df9-5ac0048bc702"
},
"metadata": {
"secrets_total":70,
"secrets_usage":25,
"secrets_allowance":100,
"secret_requests_total":10,
"secret_requests_allowance":100
}
}
List Secret Requests
Request type: GET
Request path: https://password.link/api/secret_requests
Status code of successful query: 200 OK
Limit and offset
The maximum amount of returned results 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/secret_requests?offset=50
Query example
curl -H "Authorization: ApiKey private_key_abcd" https://password.link/api/secret_requests
Response example
{
"data":[
{
"id":"5e976e6e-d205-4f58-8df9-5ac0048bc702",
"description":"example",
"message":"example",
"expiration":3,
"limit":1,
"send_to_email":"example@example.com",
"secret_description":"example",
"secret_message":"example",
"secret_expiration":"example",
"secret_max_views":4,
"secret_password":false,
"template_id":null
}
],
"metadata": {
"secrets_total":70,
"secrets_usage":25,
"secrets_allowance":100,
"secret_requests_total":10,
"secret_requests_allowance":100
}
}
Delete Secret Request
Request type: DELETE
Request path: https://password.link/api/secret_requests/<id>
Status code of successful query: 200 OK
Delete a Secret Request.
Query example
curl -H "Authorization: ApiKey private_key_abcd" \
-X DELETE https://password.link/api/secret_requests/5e976e6e-d205-4f58-8df9-5ac0048bc702
Response example
{
"data": null,
"metadata": {
"secrets_total":70,
"secrets_usage":25,
"secrets_allowance":100,
"secret_requests_total":10,
"secret_requests_allowance":100}
}