• Integration Docs

Partner API Integration Guide — Shortlet

List approved Shortlet properties, show their details on your platform, and securely hand a verified customer over to Shortlet to complete a reservation.

API Versionv1
AuthenticationAPI key + HMAC-SHA256
Base URLapi-staging.shortlet.app
01

Integration overview

The Shortlet Partner API lets an approved partner:

  • Retrieve approved Shortlet properties.
  • Display property details on the partner platform.
  • Securely hand a partner customer over to Shortlet to complete a reservation.

The three endpoints

MethodEndpoint
GET/v1/api/partner/properties
GET/v1/api/partner/properties/:id
POST/v1/api/partner/handoffs/reservation

What Shortlet stays responsible for

  • Property availability
  • Customer creation or login on Shortlet
  • Reservation dates and guest selection
  • Booking creation
  • Payment
  • Booking lifecycle
02

Partner credentials

After Shortlet approves your partner application, log in to Shortlet and open the partner dashboard. From the dashboard, copy your API key and handoff secret.

API key format

text
shk_live_<8_hex_prefix>_<32_hex_secret>

# Example
shk_live_a1b2c3d4_0123456789abcdef0123456789abcdef

Credential responsibilities

CredentialPurpose
API keyAuthenticates your backend and identifies your partner account
Handoff secretSigns reservation handoff data to prevent tampering
Security Requirements
  • Store both credentials only on your backend.
  • Never embed them in frontend JavaScript or mobile app code.
  • Never send the handoff secret to the customer's browser.
  • Do not commit credentials to source control.
  • Use separate API keys for development and production where available.
  • If an API key is exposed, revoke it from the partner dashboard and generate a new one.
03

API base URL

Use the Shortlet staging API base URL:

URL
https://api-staging.shortlet.app
04

Authentication header

All three endpoints require the API key in the X-API-Key header.

bash
X-API-Key: shk_live_<8_hex_prefix>_<32_hex_secret>

# Example
curl "https://api-staging.shortlet.app/v1/api/partner/properties" \
  -H "X-API-Key: {{PARTNER_API_KEY}}"
Do not use the partner API key as a Bearer token.
05

List approved properties

Returns approved Shortlet properties that may be displayed on the partner platform.

GET /v1/api/partner/properties

Pagination

ParameterTypeDescription
pagenumberPage to retrieve. Defaults to page 1
limitnumberNumber of properties per page
sortstringSort expression, for example -createdAt or amount
GET /v1/api/partner/properties?page=2&limit=20&sort=-createdAt

Filters

ParameterTypeExample
citystringLagos
statestringLagos
countrystringNigeria
typestringApartment
minPricenumber50000
maxPricenumber250000
guestsnumber2
bedroomsnumber2
bedsnumber2
bathroomsnumber1
fromISO date2026-07-10
toISO date2026-07-13

Example requests

GET /v1/api/partner/properties?type=Apartment
GET /v1/api/partner/properties?city=Lagos&page=1&limit=20
GET /v1/api/partner/properties?country=Nigeria&state=Lagos&type=Apartment

Example curl request

bash
curl --get "https://api-staging.shortlet.app/v1/api/partner/properties" \
  -H "X-API-Key: {{PARTNER_API_KEY}}" \
  --data-urlencode "country=Nigeria" \
  --data-urlencode "city=Lagos" \
  --data-urlencode "type=Apartment" \
  --data-urlencode "page=1" \
  --data-urlencode "limit=20"

Example response

json
{
  "status": true,
  "statusCode": 200,
  "message": "Properties retrieved successfully.",
  "data": {
    "result": [
      {
        "id": "698d89406f2b670830021be3",
        "name": "Ocean View Apartment",
        "slug": "ocean-view-apartment",
        "type": "Apartment",
        "location": {
          "city": "Lagos",
          "state": "Lagos",
          "country": "Nigeria",
          "address": "Victoria Island",
          "coordinates": {
            "type": "Point",
            "coordinates": [3.421, 6.428]
          }
        },
        "amount": 150000,
        "cautionFee": 25000,
        "minimumDays": 1,
        "photos": ["https://example.com/property.jpg"],
        "mainImage": "https://example.com/property.jpg",
        "amenities": [{ "id": "amenity-id", "name": "Wi-Fi", "slug": "wi-fi" }],
        "description": {
          "guests": 2,
          "bedrooms": 1,
          "beds": 1,
          "bathrooms": 1
        },
        "ratingsAverage": 4.5,
        "ratingsQuantity": 20,
        "discounts": [],
        "bookedDates": []
      }
    ],
    "count": 1
  },
  "jwt": null
}
Store the returned id. It is required when fetching a single property and creating a reservation handoff.
06

Fetch one property

Returns the details of one approved Shortlet property.

GET /v1/api/partner/properties/:id

Example

bash
curl "https://api-staging.shortlet.app/v1/api/partner/properties/698d89406f2b670830021be3" \
  -H "X-API-Key: {{PARTNER_API_KEY}}"

Response

json
{
  "status": true,
  "statusCode": 200,
  "message": "Property retrieved successfully.",
  "data": {
    "id": "698d89406f2b670830021be3",
    "name": "Ocean View Apartment",
    "type": "Apartment",
    "location": {
      "city": "Lagos",
      "state": "Lagos",
      "country": "Nigeria"
    },
    "amount": 150000,
    "photos": [],
    "amenities": [],
    "bookedDates": []
  },
  "jwt": null
}

Not found

json
{
  "status": false,
  "statusCode": 404,
  "message": "Property was not found."
}
07

Reservation handoff

When a customer clicks Reserve Apartment on the partner platform, your frontend should call your own backend. Your backend must generate an HMAC-SHA256 package and transfer payload configurations directly to Shortlet securely.

POST /v1/api/partner/handoffs/reservation

Request body

json
{
  "propertyId": "698d89406f2b670830021be3",
  "uniqueUserId": "customer-12345",
  "timestamp": 1782230400,
  "nonce": "6cf95610-bb34-45c0-a838-f9e3219fd741",
  "shortletDigest": "64_character_lowercase_hex_digest",
  "userBio": {
    "name": "Jane Doe",
    "email": "[email protected]",
    "phone": "+2348012345678",
    "isKycVerified": true
  }
}

Field reference

FieldRequiredDescription
propertyIdYesShortlet property ID returned by the property endpoints
uniqueUserIdYesStable customer identifier from your system
timestampYesCurrent Unix timestamp in seconds
nonceYesUnique random value for this request
shortletDigestYesHMAC-SHA256 digest encoded as lowercase hexadecimal
userBio.nameYesCustomer's first and last name
userBio.emailYesCustomer email address
userBio.phoneYesCustomer phone number, preferably international format
userBio.isKycVerifiedYesWhether your platform has verified the customer
08

HMAC signature

Canonical string

canonical pattern
propertyId=698d89406f2b670830021be3
uniqueUserId=customer-12345
timestamp=1782230400
nonce=6cf95610-bb34-45c0-a838-f9e3219fd741
name=Jane Doe
[email protected]
phone=+2348012345678
isKycVerified=true

Node.js

javascript
import { createHmac, randomUUID } from 'node:crypto';

function buildHandoffPayload({ propertyId, customer, handoffSecret }) {
  const timestamp = Math.floor(Date.now() / 1000);
  const nonce = randomUUID();

  const payload = {
    propertyId,
    uniqueUserId: customer.id,
    timestamp,
    nonce,
    userBio: {
      name: customer.name,
      email: customer.email,
      phone: customer.phone,
      isKycVerified: customer.isKycVerified,
    },
  };

  const canonicalString = [
    `propertyId=${payload.propertyId}`,
    `uniqueUserId=${payload.uniqueUserId}`,
    `timestamp=${payload.timestamp}`,
    `nonce=${payload.nonce}`,
    `name=${payload.userBio.name}`,
    `email=${payload.userBio.email}`,
    `phone=${payload.userBio.phone}`,
    `isKycVerified=${payload.userBio.isKycVerified}`,
  ].join('\n');

  const shortletDigest = createHmac('sha256', handoffSecret).update(canonicalString, 'utf8').digest('hex');

  return { ...payload, shortletDigest };
}

Python

python
import hashlib
import hmac
import time
import uuid


def build_handoff_payload(property_id, customer, handoff_secret):
    timestamp = int(time.time())
    nonce = str(uuid.uuid4())

    user_bio = {
        "name": customer["name"],
        "email": customer["email"],
        "phone": customer["phone"],
        "isKycVerified": customer["isKycVerified"],
    }

    canonical_string = "\n".join([
        f"propertyId={property_id}",
        f"uniqueUserId={customer['id']}",
        f"timestamp={timestamp}",
        f"nonce={nonce}",
        f"name={user_bio['name']}",
        f"email={user_bio['email']}",
        f"phone={user_bio['phone']}",
        f"isKycVerified={str(user_bio['isKycVerified']).lower()}",
    ])

    shortlet_digest = hmac.new(
        handoff_secret.encode("utf-8"),
        canonical_string.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()

    return {
        "propertyId": property_id,
        "uniqueUserId": customer["id"],
        "timestamp": timestamp,
        "nonce": nonce,
        "shortletDigest": shortlet_digest,
        "userBio": user_bio,
    }

PHP

php
<?php

$canonicalString = implode("\n", [
    "propertyId={$payload['propertyId']}",
    "uniqueUserId={$payload['uniqueUserId']}",
    "timestamp={$payload['timestamp']}",
    "nonce={$payload['nonce']}",
    "name={$payload['userBio']['name']}",
    "email={$payload['userBio']['email']}",
    "phone={$payload['userBio']['phone']}",
    "isKycVerified=" . ($payload['userBio']['isKycVerified'] ? 'true' : 'false'),
]);

$payload['shortletDigest'] = hash_hmac(
    'sha256',
    $canonicalString,
    $handoffSecret
);
09

Submit the handoff

Node.js fetch example

javascript
const payload = buildHandoffPayload({
  propertyId,
  customer,
  handoffSecret: process.env.SHORTLET_HANDOFF_SECRET,
});

const response = await fetch(`${process.env.SHORTLET_API_BASE_URL}/v1/api/partner/handoffs/reservation`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': process.env.SHORTLET_PARTNER_API_KEY,
  },
  body: JSON.stringify(payload),
});

const result = await response.json();

if (!response.ok) {
  throw new Error(result.message || 'Unable to create Shortlet handoff');
}

return result.data.redirectUrl;

Example response

json
{
  "status": true,
  "statusCode": 201,
  "message": "Reservation handoff created successfully.",
  "data": {
    "token": "4e7b462e8732b10bbbdf3532e3a946d37994a2ff234093c0aa1a08cdd2c47b7c",
    "redirectUrl": "https://shortlet.app/listings/698d89406f2b670830021be3/?pageView=live&partner=true&token=4e7b462e8732b10bbbdf3532e3a946d37994a2ff234093c0aa1a08cdd2c47b7c",
    "expiresAt": "2026-06-23T12:10:00.000Z"
  },
  "jwt": null
}
10

Error handling

Missing API key

json
{ "statusCode": 401, "message": "Missing API key." }

Invalid API key

json
{ "statusCode": 401, "message": "Invalid API key." }

Revoked or expired API key

json
{ "statusCode": 401, "message": "API key has been revoked." }

{ "statusCode": 401, "message": "API key has expired." }

Invalid signature

json
{ "statusCode": 401, "message": "Invalid reservation handoff digest." }
11

Recommended environment variables

bash
SHORTLET_API_BASE_URL=https://api-staging.shortlet.app
SHORTLET_PARTNER_API_KEY=shk_live_...
SHORTLET_HANDOFF_SECRET=...