> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://www.truework.com/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://www.truework.com/docs/_mcp/server.

Truework API responses include a signature header that can be used to verify the authenticity of the API response.

## Public keys

Truework's public key used to validate the signature will be published on this page.

| KEY ID                  | NOT VALID BEFORE | NOT VALID AFTER | Base 64 Encoded Value                          |
| ----------------------- | ---------------- | --------------- | ---------------------------------------------- |
| `tw-2021-11-11`         | 2021-11-11       | N/A             | `Ad14e4K+/6At51wUH2B3M4ONNlRmLiSpI4NUAl+IcyA=` |
| `tw-sandbox-2021-11-11` | 2021-11-11       | N/A             | `yJBzPH0p+gdRuj4qnjU1+ob0AH52gHRDXxnDW1xGaYk=` |

## Signature

The signature is included as a header named `X-Truework-Signature` and includes the information required to verify the signature.
The signature has three parts: `keyId`, `headers` and `signature`. For example:

```text
X-Truework-Signature: keyId="tw-2021-11-11", headers="date content-length", signature="some_long_signature_b64_encoded"
```

| Signature Part | Details                                                                                                                                         |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `keyId`        | Refers to the key id of the public key that can be used to verify the signature                                                                 |
| `headers`      | Refers to which response headers were used in the construction of the signature. Each header key is lower cased, separated by a space character |
| `signature`    | Refers to the actual signature that will be verified. Truework uses Ed25519 to generate the signature                                           |

## Verifying the signature

Based on the `headers` part of the `Signature` header, we can construct the message that we will use to verify the signature.
The message contains the following components, separated by `\n` characters:

1. Header key and value, in the format `headerKey: headerValue`
2. Response body

For example, the message constructed for the API response:

```text
Date:
Content-Length: 50
{"responseKey": "responseValue"}
```

Message constructed:

```text
date: Fri, 12 Nov 2021 19:28:59 GMT\n
content-length: 50\n
{"responseKey": "responseValue"}
```

Using the constructed message, public key and signature, we can verify the response payload.

## Code example

To verify the signature, we first parse the signature header to extract its parts:

```python
signature = response.get_header("X-Truework-Signature")

signature_parts = {
    i.split("=", 1)[0].strip(): i.split("=", 1)[1].strip('"')
    for i in signature.split(",")
}
key_id = signature_parts["keyId"]
part_headers = signature_parts["headers"]
part_signature = signature_parts["signature"]
```

We then construct the message that we will use to verify the signature:

```python
def get_headers_for_message(headers_part: str, response) -> dict:
    headers = headers_part.split(" ")
    return {i.lower(): response.get_header(i) for i in headers}


def get_message(headers: dict, body: str):
    headers_to_sign = "\n".join([f"{key}: {value}" for key, value in headers.items()])
    message = headers_to_sign + "\n" + body if headers_to_sign else body
    return message


headers_for_message = get_headers_for_message(part_headers, response)
message = get_message(headers_for_message, response.body)
```

Finally, we verify the message using the constructed message, Truework's public key, and `signature` part:

```python
from nacl.encoding import Base64Encoder
from nacl.exceptions import BadSignatureError
from nacl.signing import VerifyKey


def verify_signature(message: str, verify_key_bytes: bytes, signature: str):
    verify_key = VerifyKey(verify_key_bytes)
    signature_b64_bytes = signature.encode()
    signature_bytes = Base64Encoder.decode(signature_b64_bytes)
    return verify_key.verify(message.encode(), signature_bytes)


verify_key_b64_bytes = "tw_public_key_b64_encoded".encode()
verify_key_bytes = Base64Encoder.decode(verify_key_b64_bytes)
verify_signature(message, verify_key_bytes=verify_key_bytes, signature=part_signature)
```