Back to Home
Developer Reference

CFR API Documentation

Complete reference for the Carbon Fiber Repair chatbot and WhatsApp Business API integration. Every backend method is documented with its Candid signature, request/response schemas, and copy-paste example payloads. Use this page to wire up Wati, AiSensy, or Interakt for outbound status messages and inbound webhook ingestion.

Query#1

getRepairStatusByJobSheetNo

Candid Signature

getRepairStatusByJobSheetNo(jobSheetNo : Text) -> async ?RepairStatusPayload

Look up a single repair by its Job Sheet Number (format JS-YYYY-NNNN). Returns null when no job sheet matches.

Response Schema

type RepairStatusPayload = {
  jobSheetNo : Text;
  customerFirstName : Text;
  status : Text;
  statusHistory : [StatusHistoryPublic];
  estimatedCost : Nat;
  paymentStatus : Text;
  estimatedDeliveryDate : ?Text;
  courierTracking : ?CourierTrackingPublic;
};

type StatusHistoryPublic = {
  status : Text;
  timestamp : Int;
};

type CourierTrackingPublic = {
  trackingNumber : Text;
  trackingUrl : ?Text;
};

Example Request

// Candid call
await actor.getRepairStatusByJobSheetNo("JS-2026-0042");

Example Response

[
  {
    "jobSheetNo": "JS-2026-0042",
    "customerFirstName": "Rahul",
    "status": "In Repair",
    "statusHistory": [
      { "status": "Booked", "timestamp": 1752019200 },
      { "status": "In Repair", "timestamp": 1752192000 }
    ],
    "estimatedCost": 400,
    "paymentStatus": "Pending",
    "estimatedDeliveryDate": "2026-07-18",
    "courierTracking": null
  }
]
Query#2

getRepairStatusByMobile

Candid Signature

getRepairStatusByMobile(mobile : Text) -> async [RepairStatusPayload]

Look up every repair linked to a 10-digit mobile number. Returns an array of all matching job sheets (empty if none).

Response Schema

type RepairStatusPayload = {
  jobSheetNo : Text;
  customerFirstName : Text;
  status : Text;
  statusHistory : [StatusHistoryPublic];
  estimatedCost : Nat;
  paymentStatus : Text;
  estimatedDeliveryDate : ?Text;
  courierTracking : ?CourierTrackingPublic;
};

type StatusHistoryPublic = {
  status : Text;
  timestamp : Int;
};

type CourierTrackingPublic = {
  trackingNumber : Text;
  trackingUrl : ?Text;
};

Example Request

// Candid call
await actor.getRepairStatusByMobile("9441135023");

Example Response

[
  {
    "jobSheetNo": "JS-2026-0042",
    "customerFirstName": "Rahul",
    "status": "In Repair",
    "statusHistory": [],
    "estimatedCost": 400,
    "paymentStatus": "Pending",
    "estimatedDeliveryDate": "2026-07-18",
    "courierTracking": null
  },
  {
    "jobSheetNo": "JS-2026-0031",
    "customerFirstName": "Rahul",
    "status": "Delivered",
    "statusHistory": [],
    "estimatedCost": 250,
    "paymentStatus": "Paid",
    "estimatedDeliveryDate": "2026-06-30",
    "courierTracking": {
      "trackingNumber": "DLV1234567890",
      "trackingUrl": "https://www.delhivery.com/track/DLV1234567890"
    }
  }
]
Query#3

getRepairCharges

Candid Signature

getRepairCharges() -> async RepairChargesCatalog

Returns the full repair pricing catalog with both customer and dealer rates, inclusions, and currency.

Response Schema

type RepairChargesCatalog = {
  entries : [RepairChargeEntry];
  currency : Text;
};

type RepairChargeEntry = {
  repairType : Text;
  displayName : Text;
  customerPrice : Nat;
  dealerPrice : Nat;
  inclusions : [Text];
};

Example Request

// Candid call
await actor.getRepairCharges();

Example Response

{
  "entries": [
    {
      "repairType": "steelPiece",
      "displayName": "Steel Piece",
      "customerPrice": 250,
      "dealerPrice": 150,
      "inclusions": ["Steel reinforcement", "Finish polish"]
    },
    {
      "repairType": "airCrackCarbonBinding",
      "displayName": "Air Crack Carbon Binding",
      "customerPrice": 300,
      "dealerPrice": 220,
      "inclusions": ["Carbon binding layer", "Frame alignment"]
    },
    {
      "repairType": "carbonFiberRepair",
      "displayName": "Carbon Fiber Repair",
      "customerPrice": 400,
      "dealerPrice": 290,
      "inclusions": ["Carbon fiber patch", "Resin cure", "Surface sand"]
    },
    {
      "repairType": "woodenHandleChange",
      "displayName": "Wooden Handle Change",
      "customerPrice": 300,
      "dealerPrice": 200,
      "inclusions": ["Handle replacement", "Grip wrap"]
    }
  ],
  "currency": "INR"
}
Update#4

createSupportRequest

Candid Signature

createSupportRequest(mobile : Text, message : Text) -> async SupportRequest

Creates a new chatbot support request from a customer's mobile number and free-text message. Used by the in-app chatbot when a customer escalates beyond the menu.

Response Schema

type SupportRequest = {
  id : Text;
  mobile : Text;
  message : Text;
  createdAt : Int;
  resolved : Bool;
};

Example Request

// Candid call
await actor.createSupportRequest(
  "9441135023",
  "My racket JS-2026-0042 is delayed, please call back."
);

Example Response

{
  "id": "SR-2026-0017",
  "mobile": "9441135023",
  "message": "My racket JS-2026-0042 is delayed, please call back.",
  "createdAt": 1752278400,
  "resolved": false
}
Query#5

getChatbotSupportRequests

Candid Signature

getChatbotSupportRequests() -> async [SupportRequest]

Admin-only query. Returns every chatbot support request, oldest first, including resolved ones.

Response Schema

type SupportRequest = {
  id : Text;
  mobile : Text;
  message : Text;
  createdAt : Int;
  resolved : Bool;
};

Example Request

// Candid call (admin)
await actor.getChatbotSupportRequests();

Example Response

[
  {
    "id": "SR-2026-0017",
    "mobile": "9441135023",
    "message": "My racket JS-2026-0042 is delayed, please call back.",
    "createdAt": 1752278400,
    "resolved": false
  },
  {
    "id": "SR-2026-0016",
    "mobile": "9876543210",
    "message": "Need a restring quote for Yonex Astrox 88D.",
    "createdAt": 1752192000,
    "resolved": true
  }
]
Query#6

getWhatsAppConfig

Candid Signature

getWhatsAppConfig() -> async WhatsAppConfigMasked

Returns the current WhatsApp Business API configuration with the API key masked. Safe to call from the frontend.

Response Schema

type WhatsAppConfigMasked = {
  providerName : ?Text;
  apiBaseUrl : ?Text;
  defaultTemplateId : ?Text;
  enabled : Bool;
  apiKeyMasked : Text;
  webhookSecretConfigured : Bool;
};

Example Request

// Candid call
await actor.getWhatsAppConfig();

Example Response

{
  "providerName": "#wati",
  "apiBaseUrl": "https://api.wati.io/api/v1",
  "defaultTemplateId": "cfr_status_update",
  "enabled": true,
  "apiKeyMasked": "wati_••••••••••••3f9a",
  "webhookSecretConfigured": true
}
Update#7

setWhatsAppConfig

Candid Signature

setWhatsAppConfig(config : WhatsAppConfig) -> async ()

Admin-only update. Stores the full WhatsApp Business API configuration including the raw API key and webhook secret. Provider is a Motoko variant: #wati, #aiSensy, or #interakt.

Request Schema

type WhatsAppConfig = {
  providerName : { #wati; #aiSensy; #interakt };
  apiKey : Text;
  apiBaseUrl : Text;
  defaultTemplateId : Text;
  enabled : Bool;
  webhookSecret : Text;
};

Response Schema

// Returns unit () on success

Example Request

// Candid call (admin)
await actor.setWhatsAppConfig({
  providerName: { wati: null },
  apiKey: "wati_live_8f3a9b2c1d4e5f6a7b8c9d0e1f2a3b4c",
  apiBaseUrl: "https://api.wati.io/api/v1",
  defaultTemplateId: "cfr_status_update",
  enabled: true,
  webhookSecret: "cfr_webhook_secret_2026"
});

Example Response

// Unit () — empty response on success
Update#8

sendWhatsAppMessage

Candid Signature

sendWhatsAppMessage(toNumber : Text, templateName : Text, templateParams : [Text]) -> async WhatsAppSendResult

Sends a WhatsApp template message to a customer via the configured provider. Template name and params map to the provider's approved template library.

Response Schema

type WhatsAppSendResult = {
  success : Bool;
  providerMessageId : ?Text;
  error : ?Text;
};

Example Request

// Candid call
await actor.sendWhatsAppMessage(
  "919441135023",
  "cfr_status_update",
  ["JS-2026-0042", "In Repair", "2026-07-18"]
);

Example Response

{
  "success": true,
  "providerMessageId": "wamid.HBgLOTE5NDQxMTM1MDIzFQIAERgS",
  "error": null
}
Webhook#9

http_request

Candid Signature

http_request(request : WebhookRequest) -> async WebhookResponse

Canister HTTP ingress handler used as the WhatsApp inbound webhook. Validates the x-webhook-secret header against the stored secret, then ingests the inbound message. Returns 200 OK on success or 401 on a missing/invalid secret.

Request Schema

type WebhookRequest = {
  method : Text;
  url : Text;
  headers : [HttpHeader];
  body : ?Blob;
};

type HttpHeader = {
  name : Text;
  value : Text;
};

Response Schema

type WebhookResponse = {
  status : Nat;
  headers : [HttpHeader];
  body : Blob;
};

Example Request

POST <canister-url>/http_request
Headers:
  Content-Type: application/json
  x-webhook-secret: cfr_webhook_secret_2026

Body (inbound WhatsApp payload, provider-specific):
{
  "fromNumber": "919441135023",
  "messageBody": "Track JS-2026-0042",
  "receivedAt": 1752278400
}

Example Response

{
  "status": 200,
  "headers": [
    { "name": "Content-Type", "value": "application/json" }
  ],
  "body": "{\"ok\":true}"
}

WhatsApp Webhook

Inbound message ingestion endpoint

The canister exposes an HTTP ingress handler at /http_request. Configure your WhatsApp provider (Wati, AiSensy, or Interakt) to POST inbound messages to this URL. Every request must include the x-webhook-secret header matching the value stored via setWhatsAppConfig. Requests with a missing or mismatched secret receive a 401.

URL Pattern

https://<canister-id>.icp0.io/http_request

Required Headers

x-webhook-secret: <your-secret>
Content-Type: application/json

Inbound Payload Schema

{
  "fromNumber": Text,   // sender WhatsApp number, E.164
  "messageBody": Text,  // inbound message text
  "receivedAt": Int      // unix timestamp (seconds)
}

Outbound sendWhatsAppMessage Template Format

await actor.sendWhatsAppMessage(
  toNumber,        // E.164 recipient number
  templateName,    // approved template, e.g. "cfr_status_update"
  templateParams   // positional params, e.g. ["JS-2026-0042", "In Repair", "2026-07-18"]
);

Integration Checklist

Wati / AiSensy / Interakt setup steps

  1. 1

    Set the webhook URL in your provider dashboard

    Point Wati / AiSensy / Interakt to <canister-url>/http_request. The canister URL is shown in the Caffeine dashboard once deployed.

  2. 2

    Configure the webhook secret via setWhatsAppConfig

    Call setWhatsAppConfig with a strong webhookSecret. The same value must be sent by the provider in the x-webhook-secret header on every inbound request.

  3. 3

    Map template names

    Create approved WhatsApp templates in the provider dashboard (e.g. cfr_status_update) and set defaultTemplateId in setWhatsAppConfig to match. sendWhatsAppMessage uses these template names.

  4. 4

    Set the API key and base URL

    Provide the provider's live API key and REST base URL (e.g. https://api.wati.io/api/v1) in setWhatsAppConfig. getWhatsAppConfig returns the key masked for safe frontend display.

  5. 5

    Verify inbound ingestion

    Send a test WhatsApp message to your business number. The canister's http_request handler validates the secret, ingests the message, and returns 200 OK. A 401 means the secret mismatch.

Provider Notes

Wati, AiSensy, and Interakt specifics

Wati

https://api.wati.io/api/v1

Webhook payload uses waId and text.body. Template send endpoint: /sendTemplateMessage. Set providerName to #wati.

AiSensy

https://api.aisensy.com/v1

Webhook payload uses from and message.text. Template send endpoint: /campaigns/send-template. Set providerName to #aiSensy.

Interakt

https://api.interakt.ai/v1/public

Webhook payload uses phone and message.text. Template send endpoint: /message/send_template. Set providerName to #interakt.

Need help integrating? Contact CFR at 9441135023 or cfsportsrepair@gmail.com.