Skip to content

Your Backend

Overview

Your backend is a critical component in any integration with CoKeeps Hot Wallet. It acts as the bridge between CoKeeps and your own platform, coordinating data, enforcing your business rules, and ensuring that all actions taken through the Hot Wallet remain aligned with your organisational policies.

For end-user access and authorisation, CoKeeps Hot Wallet relies on your backend to determine:

  • Whether a user exists and is active
  • The user’s roles and permissions
  • Any limits or policy conditions that should apply to transfers (such as per-user transfer limits and whether a destination address is approved or allowed)

This design ensures that CoKeeps operates strictly within the guardrails you define, rather than maintaining its own separate user authority model.

To enable this integration, your backend only needs to expose four REST API endpoints. These endpoints handle the required lookups, validations, and decisions that CoKeeps Hot Wallet calls into when processing actions.

IMPORTANT

The availability and reliability of your backend service are essential. If your backend is down or degraded, Hot Wallet operations (such as withdrawals, sweeps, and checks against your policies) may be delayed or blocked. We strongly recommend treating these endpoints as part of your critical infrastructure, with appropriate monitoring, redundancy, and uptime targets.

Preparations

Network

For secure communication between your backend and CoKeeps Hot Wallet, we strongly recommend establishing a Virtual Private Network (VPN). This helps ensure all traffic flows within a protected network boundary, reducing exposure to the public internet and mitigating the risk of unauthorised access.

During the onboarding session between your technical team and CoKeeps, VPN options, configuration details, and connectivity requirements can be discussed and finalised.

If a VPN is not feasible, the minimum requirement is to use TLS over HTTPS for all communication. This provides encryption in transit, protecting data from interception and tampering.

Regardless of the network model you choose, it is essential to:

  • Restrict inbound requests to your backend endpoints so that they are only accepted from authorised CoKeeps Hot Wallet servers.
  • Enforce encryption for all communication channels between CoKeeps and your systems.

These measures help protect sensitive data and maintain the integrity of both your environment and the CoKeeps integration.

Data Encryption

All request and response payloads exchanged between CoKeeps Hot Wallet and your backend are encrypted using the Advanced Encryption Standard (AES). AES is chosen for its strong security guarantees and wide support across common backend technologies.

Both CoKeeps Hot Wallet and your backend service use the same shared secret key for encryption and decryption of payloads. This key is generated and provided by CoKeeps during onboarding, and must then be stored and managed securely by your organisation in line with your internal key management practices.

Below is an example demonstrating encryption in JavaScript:

javascript
const AES = require("crypto-js/aes");

const payload = "some_arbitrary_string";
const secret = "shared_secret";
const ciphertext = AES.encrypt(payload, secret).toString();

console.log(ciphertext)

The output of that example is the ciphertext:

U2FsdGVkX18fBdZ7xNi0aOX9b5M97C7+N2m2ZBx2/dzEal6SuHse8NOkbCV7kJ54

This ciphertext is the encrypted form of the sample payload using the shared secret key. To validate your implementation, you should be able to decrypt this ciphertext with your backend code (using the agreed AES configuration and shared secret) and obtain the original plaintext payload.

Receiving Endpoints

All requests sent from CoKeeps Hot Wallet to your backend follow the structure below:

JSON
{
    ciphertext: AES.encrypt(JSON.stringify({
        // Payload
    }))
}

All response payloads from your backend should follow the following structure:

Success:

JSON
{
    success: true,
    message: "OK",
    ciphertext: AES.encrypt(JSON.stringify({
        // Payload
    }))
}

Error:

JSON
{
    success: false,
    message: "Error message..",
    ciphertext: AES.encrypt(JSON.stringify({}))
}

NOTE

Even when there is no data to return, an encrypted empty object must still be sent so that CoKeeps Hot Wallet can decrypt it and verify that the response originated from your backend.

User Verify

Incoming request:

POST https://<your-backend-url>/user_verify

JSON
{
	"hashedUsername": "string",
	"hashedPassword": "string",
	"x": "string"
}
  • hashedUsername: SHA-256 hash of the user's login identifier (e.g. username, email, or other unique ID). Do not remove or invalidate this field if the user resets their credentials.

  • hashedPassword: SHA-256 hash of the user's password or secret. This value will be used by your backend to verify that the supplied credentials match an active user who is allowed to access the CoKeeps Hot Wallet. Do not remove or invalidate this field when the user resets their credentials (instead, update your stored password reference accordingly).

  • x: Arbitrary value generated by CoKeeps Hot Wallet for this verification request. Your system must combine this value with a salt to derive the key that you will return in the response. Do not store this value or the generated key.

Warning: Your user management system must never store raw (plaintext) passwords in the database. Always store passwords, at minimum - using secure, one-way hashing (and salting where applicable). This ensures that, even if your database is compromised, attackers cannot directly recover user passwords.

Response payload:

JSON
{
	"uid": "string",
	"key": "string"
}
  • uid: The user's UUID in your system that corresponds to the provided hashedUsername and hashedPassword.

  • key: A deterministic, hashed value derived from the x value and a salt. This is used by CoKeeps Hot Wallet to verify the integrity of the request/response pair and to bind the session to your user. The value must always be computed the same way for the same user and x input.

Example generation of key in JavaScript:

javascript
const SHA256 = require("crypto-js/sha256");

// You can use a dedicated salt based on the UID,
// provided it remains consistently associated with that UID.
const salt = "super_secret_uid_salt";
const x = "arbitrary_value_passed_by_cokeeps_hot_wallet";

const key = SHA256(salt + x).toString();

console.log(key);
// 85121f6138cdc292b8f483bba88779df53c74ef3df47c5b318c111f8d4d78479

This example will always produce the key value:

85121f6138cdc292b8f483bba88779df53c74ef3df47c5b318c111f8d4d78479

IMPORTANT

The key must be deterministic and consistent for a given user and x to prevent mismatches that could lead to loss of access or, in worst cases, loss of digital assets.

Machine Verify

Incoming request:

POST https://<your-backend-url>/machine_verify

JSON
{
    "machine_id": "string",
    "x": "string"
}

This endpoint serves a similar purpose to User Verify, but is specifically intended for persistent accounts and machines. Optionally, your backend would maintain a static, trusted list of machine_id values for accounts use for automation; this list is normally established during the initial integration and should not change frequently.

  • machine_id: Unique identifier of the machine.

  • x: Arbitrary value generated by CoKeeps Hot Wallet for this verification request. Your system must use this value, combined with a salt, to derive the key returned in the response. Do not store this value or the generated key.

Response payload:

JSON
{
    "key": "string"
}
  • key: A deterministic hashed value derived from x and a salt. CoKeeps Hot Wallet uses this to verify the integrity of the request-response pair and to confirm that the machine is recognised and trusted by your backend. The value must always be computed the same way for the same user and x input.

Example generation of key in JavaScript:

javascript
const SHA256 = require("crypto-js/sha256");

// You can use a dedicated salt based on the Machine ID,
// provided it remains consistently associated with that Machine ID.
const salt = "super_secret_machine_salt";
const x = "arbitrary_value_passed_by_cokeeps_hot_wallet";

const key = SHA256(salt + x).toString();

console.log(key);
// 7097937d1a6d41c3a727ed8ac6d5d6af479b8d53ba74ae324ec2de34f80fc283

This example always produces the key:

7097937d1a6d41c3a727ed8ac6d5d6af479b8d53ba74ae324ec2de34f80fc283

IMPORTANT

The key must be consistent and deterministic for a given machine_id and x. Any inconsistency may cause verification failures and, in worst cases, could affect access to digital assets.

Read

The read endpoint allows CoKeeps Hot Wallet to retrieve the role, limits, and destination controls for a user, using the user's UUID (uid) as the primary reference. This endpoint is consulted before write operations (e.g. transfers, smart contract interactions, omnibus withdrawals) to ensure actions comply with your policies.

Your backend is expected to classify every user into one of two roles for Hot Wallet write operations:

  • signer: Standard end user with limited permissions.

  • admin: Operational/administrative user with all signer capabilities plus additional privileges, depending on your platform design.

Incoming request:

POST https://<your-backend-url>/read

JSON
{
    "uid": "string",
    "reference_id": "string" // Passed along
}
  • uid: UUID of the user in your system.

  • reference_id: A value passed through from the originating operation (e.g. Transfer Transaction, Smart Contract Transaction, Omnibus Withdrawal). Use this value to correlate the request with your own internal record and to protect against unauthorised or replayed API calls from the frontend.

Basic response (role only):

For operations that only need to know the user's role, the minimum response is:

JSON
{
    "role": "signer", // or "admin"
    "limits": [],
    "destinations": []
}

Response for transfer-related operations:

When enforcing transfer limits and destination rules, more detail is required:

JSON
{
    "role": "signer", // or "admin"
    "limits": [
        {
            "chain_id": "bitcoin",
            "balance": "0.1",
            "limit": "0.005"
        },
        {
            "chain_id": "solana",
            "balance": "20.113",
            "limit": "5.0"
        }
    ],
    "destinations": [
        {
            "chain_id": "bitcoin",
            "address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
        },
        {
            "chain_id": "solana",
            "address": "2wmVCSfPxGPjrnMMn7rchp4uaeoTqN39mXFC2zhPdri9"
        }
    ]
}
  • role: Indicates whether the user should be treated as a signer or admin for the current operation.

  • limits (array, optional): Used to define maximum transfer amounts per network (chain_id). All numeric values must be provided as decimal strings.

    For each entry:

    • limits[i].chain_id: Identifier of the blockchain/network (e.g. bitcoin, solana, an EVM chain ID).
    • limits[i].balance: The user's current effective balance for that network (as seen from your perspective), expressed as a decimal string. This should align with the balance in your ledger.
    • limits[i].limit: The maximum amount the user is allowed to transfer on that network for the current request/context.

    CoKeeps Hot Wallet will:

    • Enforce the limit you provide; and
    • Reject the request if the limit exceeds requested transfer amount.

    More sophisticated logic such as daily caps, per-transaction limits, or risk-based restrictions should be implemented by your backend. CoKeeps will simply enforce the values you return.

  • destinations (array, optional): Defines the allowed or expected destination addresses for the user.

    For each entry:

    • destinations[i].chain_id: Network identifier.
    • destinations[i].address: Destination address for that network.

    When provided, CoKeeps Hot Wallet will compare the requested destination address against this list for consistency and whitelisting purposes.

NOTE

You may include additional fields if you need more complex behaviour (e.g. flags, risk scores, per-asset rules). CoKeeps Hot Wallet will focus on the documented fields but can ignore extra data that you use internally.

Update

The update endpoint is used by CoKeeps Hot Wallet to send event notifications to your backend. These notifications allow your system to react to important lifecycle events and errors, particularly for omnibus and withdrawal flows.

CoKeeps Hot Wallet may send notifications for, but not limited to, the following events:

Omnibus-related notifications:

  • Destination address set
  • Withdrawal request created
  • Withdrawal request approved or rejected
  • Completion of the transaction process (successful or finalised state)

Failure alerts:

  • Failed transfers
  • Failed Gas Station funding attempts
  • Failed sweeping operations

NOTE

Additional event types can be introduced to support more advanced or customised workflows on your end.

Incoming request:

POST https://<your-backend-url>/update

JSON
{
    "uid": "string",
    "destination": {
        "address": "string",
        "chain_id": "string",
        "signature": "string",
        "id": "string"
    }
}
  • uid: UUID of the user in your system associated with this event (for example, the requester or beneficiary).
  • destination: Object containing information about the destination involved in the event (commonly used for omnibus and withdrawal flows):
    • destination.address: Destination address on the specified chain.
    • destination.chain_id: Identifier of the blockchain/network.
    • destination.signature: Signature provided by CKSDK to attest to the request or destination payload.
    • destination.id: Internal identifier for the destination or request (useful for correlating with your own records).

Depending on the event type, additional fields may be included in the payload (e.g. status, error details, transaction hash). Your backend should be prepared to store unknown fields.

Response payload:

JSON
{} // Empty object

Your backend should return an encrypted empty object to acknowledge receipt of the notification. This allows CoKeeps Hot Wallet to decrypt and verify that the response originated from your backend.

Delivery & Logging

If your backend does not respond successfully, CoKeeps Hot Wallet will retry the notification up to three times within a 5-minute window. After the retry limit is reached, the event is marked as undelivered on the CoKeeps side.

NOTE

We strongly recommend logging all update requests and your corresponding responses - for example, in an application log or audit trail - similar to how an operating system logs events. These logs are invaluable for debugging, compliance checks, and forensic analysis in the event of operational issues or disputes.

Secret and Salt Management

Secret

The secret is the shared key used for AES encryption and decryption of payload data exchanged between CoKeeps Hot Wallet and your backend service. If this value is ever suspected to be compromised, it must be rotated and replaced immediately.

The secret must not be hard-coded in source code or stored in version control systems. Instead, it should be:

  • Kept in a secure configuration store (e.g. secrets manager, encrypted config), and

  • Injected into your backend via restricted environment variables or equivalent secure mechanisms.

Salt

The salt is used to generate the key values described in the User Verify and Machine Verify sections. CoKeeps must never have knowledge of this value, and it must remain consistent and available for as long as the integration is active.

Key points for the salt:

  • It can be any arbitrary value, but should be long and high-entropy.
  • You may generate it, for example, by hashing a strong password with SHA-512.
  • It must be deterministic and consistent: the same salt must always be used to ensure that generated keys are reproducible.

As with the secret, the salt must not be distributed broadly or exposed to irrelevant parties, and must not be committed to source control.

Sharding Strategy

To further reduce the risk of a single point of compromise, you may optionally use Shamir Secret Sharing to split the secret or salt into multiple shards stored in different locations or held by different parties. At runtime, your backend can reconstruct the original value by combining the required number of shards.

bash
                                   ┌──────────────────┐
                               ┌───┤ Shard 1 Location
   └──────────────────┘
	┌──────────────────────┐                       
   Back-end Service   ┌──────────────────┐
                      ├───┼───┤ Shard 2 Location
    Combine 2 of 3   └──────────────────┘
	└──────────────────────┘                       
   ┌──────────────────┐
                               └───┤ Shard 3 Location
                                   └──────────────────┘

You are free to define:

  • The number of shards, and
  • The threshold (minimum number of shards required to reconstruct the value), to balance redundancy and security.

NOTE

This strategy is optional and should only be implemented if your team fully understands redundancy design and the principles of Shamir Secret Sharing. Misconfiguration can lead to loss of access if shards are misplaced or thresholds are set incorrectly.