{"openapi":"3.0.3","info":{"title":"HashTech Developer API","version":"1.0.0","description":"আপনার নিজের সফটওয়্যার থেকে ভয়েস কল দিন।\n\nপ্রতিটি কলে একটি নম্বর, একটি রেকর্ড করা বার্তা, আর চাইলে গ্রাহক কোন কী চাপলেন তা জানার ব্যবস্থা। ফলাফল আপনার সার্ভারে HMAC-সই করা webhook হিসেবে পৌঁছায়।\n\nপূর্ণ ব্যাখ্যা ও উদাহরণ: https://hashtech.com.bd/docs/api","contact":{"url":"https://hashtech.com.bd/contact"}},"servers":[{"url":"https://hashtech.com.bd/api/v1","description":"প্ল্যাটফর্মের ঠিকানা — কখনো বদলায় না"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Calls","description":"কল দেওয়া, অবস্থা দেখা, বাতিল করা"},{"name":"Audio","description":"কোন বার্তাটি বাজবে তা বাছার জন্য"},{"name":"System","description":"কী কাজ করছে কিনা"}],"paths":{"/calls":{"post":{"tags":["Calls"],"summary":"একটি কল দিন","description":"কলটি সারিতে যায় এবং সাথে সাথেই একটি id ফেরত আসে।\n\nকলিং আওয়ার্সের বাইরে বা চ্যানেল ব্যস্ত থাকলে `queued` অবস্থায় অপেক্ষা করে — ফেলে দেওয়া হয় না।\n\n**`Idempotency-Key` হেডার দিন।** একই কী নিয়ে দ্বিতীয় রিকোয়েস্ট নতুন কল বানাবে না, প্রথমটির উত্তরই ফেরত দেবে (200, `Idempotent-Replay: true`)। কী ২৪ ঘণ্টা মনে রাখা হয়।","operationId":"placeCall","security":[{"bearerAuth":["calls:write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"একই কী নিয়ে দ্বিতীয় রিকোয়েস্ট নতুন কল বানাবে না — গ্রাহক দুবার ফোন পাবেন না।\n\nকী-টি \"কোন চেষ্টা\" বোঝাবে, শুধু \"কোন অর্ডার\" নয়: একই কী দিলে ইচ্ছাকৃত রিট্রাইও আটকে যাবে, তাই ২৪ ঘণ্টার মধ্যে আবার ডাকতে চাইলে `order-8842-attempt-2` জাতীয় কী দিন।\n\nএকই নম্বরে বারবার কল করায় কোনো বাধা নেই — নম্বর দেখে কিছু আটকানো হয় না, শুধু একই কী আটকানো হয়।","schema":{"type":"string","maxLength":100},"example":"order-8842-attempt-1"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceCallRequest"},"examples":{"simple":{"summary":"শুধু বার্তা বাজানো","value":{"to":"01712345678","audio_id":12,"reference":"invoice-8842","webhook_url":"https://shop.example.com/hashtech"}},"confirm":{"summary":"অর্ডার কনফার্ম — ১ চাপলে হ্যাঁ, ২ চাপলে না","value":{"to":"01712345678","audio_id":12,"reference":"order-8842","gather":{"digits":["1","2"],"repeat":2,"timeout":7},"webhook_url":"https://shop.example.com/hashtech","expires_in_minutes":120}},"agent":{"summary":"১ চাপলে এজেন্টের সাথে কথা","value":{"to":"01712345678","audio_id":12,"gather":{"digits":["1","2"]},"connect":{"on_digit":"1","queue_id":4}}},"menu":{"summary":"বার্তার পর পুরো IVR মেনু","value":{"to":"01712345678","audio_id":12,"ivr_id":3}}}}}},"responses":{"202":{"description":"কলটি গ্রহণ করা হয়েছে","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"}}}},"200":{"description":"একই Idempotency-Key — আগের কলটিই ফেরত","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"tags":["Calls"],"summary":"কলের তালিকা","operationId":"listCalls","security":[{"bearerAuth":["calls:read"]}],"parameters":[{"name":"reference","in":"query","required":false,"description":"আপনার নিজের রেফারেন্স দিয়ে খুঁজুন","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["queued","ringing","answered","busy","no_answer","failed","cancelled","expired"]}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1}}],"responses":{"200":{"description":"কলের তালিকা","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/calls/{id}":{"get":{"tags":["Calls"],"summary":"একটি কলের অবস্থা","operationId":"getCall","security":[{"bearerAuth":["calls:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"কলের সংখ্যাসূচক আইডি — উত্তরের `call_1042`-এর `1042` অংশ।","schema":{"type":"integer"}}],"responses":{"200":{"description":"কলটির বর্তমান অবস্থা","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/calls/{id}/cancel":{"post":{"tags":["Calls"],"summary":"কল বাতিল করুন","description":"শুধু `queued` অবস্থায়। ফোন বাজতে শুরু করলে আর বাতিল করা যায় না।","operationId":"cancelCall","security":[{"bearerAuth":["calls:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"কলের সংখ্যাসূচক আইডি — উত্তরের `call_1042`-এর `1042` অংশ।","schema":{"type":"integer"}}],"responses":{"200":{"description":"কলটি বাতিল হয়েছে","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}}},"/audios":{"get":{"tags":["Audio"],"summary":"আপনার প্রস্তুত অডিওর তালিকা","description":"শুধু প্রস্তুত অডিও আসে। আপলোডের পর রূপান্তরে কয়েক সেকেন্ড লাগে; সেই সময়টুকু অডিওটি তালিকায় থাকে না, কারণ বাজানোর মতো ফাইল তখনো নেই।","operationId":"listAudios","security":[{"bearerAuth":["audios:read"]}],"responses":{"200":{"description":"অডিওর তালিকা","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/ping":{"get":{"tags":["System"],"summary":"কী কাজ করছে কিনা","description":"কোনো অনুমতি (scope) লাগে না — তাই হেলথ চেকে ব্যবহার করা যায়।","operationId":"ping","responses":{"200":{"description":"কী ঠিক আছে","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","example":true},"account":{"type":"string","example":"রহিম স্টোর"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"পোর্টালের ডেভেলপার API পাতায় তৈরি করা কী।\n\n`Authorization: Bearer kp_xxxxxxxxx_…` — কী-টি একবারই দেখানো হয়।"}},"schemas":{"PlaceCallRequest":{"type":"object","required":["to","audio_id"],"properties":{"to":{"type":"string","description":"বাংলাদেশি মোবাইল নম্বর। যেকোনো চেনা রূপে দিন — `01712345678`, `+8801712345678`, `8801712345678`।","example":"01712345678"},"audio_id":{"type":"integer","description":"কোন বার্তাটি বাজবে। `GET /audios` অথবা পোর্টাল থেকে নিন।","example":12},"reference":{"type":"string","maxLength":100,"nullable":true,"description":"আপনার নিজের আইডি — webhook-এ হুবহু ফেরত আসে। আপনার ডেটাবেসে কলটি মেলানোর একমাত্র চাবি।","example":"order-8842"},"webhook_url":{"type":"string","format":"uri","maxLength":500,"nullable":true,"description":"কল শেষ হলে ফলাফল এখানে POST হবে। **https বাধ্যতামূলক**, এবং পাবলিক ঠিকানা হতে হবে (লোকাল/প্রাইভেট নেটওয়ার্ক গ্রহণ করা হয় না)।","example":"https://shop.example.com/hashtech"},"expires_in_minutes":{"type":"integer","minimum":1,"maximum":10080,"default":1440,"description":"এর মধ্যে কল দেওয়া না গেলে `expired` হবে এবং webhook যাবে। রাতের অর্ডার সকালে কনফার্ম করা অর্থহীন হলে কম দিন।"},"gather":{"$ref":"#/components/schemas/Gather"},"connect":{"$ref":"#/components/schemas/Connect"},"ivr_id":{"type":"integer","nullable":true,"description":"বার্তার পর আপনার নিজের IVR মেনু চালু হবে। `gather`/`connect`-এর সাথে একসাথে দেওয়া যাবে না।"}}},"Gather":{"type":"object","nullable":true,"description":"বার্তার পর একটি কী-এর জন্য অপেক্ষা করুন।","required":["digits"],"properties":{"digits":{"type":"array","minItems":1,"maxItems":12,"items":{"type":"string","enum":["0","1","2","3","4","5","6","7","8","9","*","#"]},"example":["1","2"]},"repeat":{"type":"integer","minimum":1,"maximum":5,"default":2,"description":"কেউ না চাপলে বার্তা আর কতবার বাজবে"},"timeout":{"type":"integer","minimum":3,"maximum":30,"default":7,"description":"প্রতিবার কত সেকেন্ড অপেক্ষা"}}},"Connect":{"type":"object","nullable":true,"description":"নির্দিষ্ট কী চাপলে গ্রাহককে সরাসরি এজেন্টের সাথে যুক্ত করুন।\n\n`gather` ছাড়া ব্যবহার করা যায় না, আর `on_digit` অবশ্যই `gather.digits`-এর একটি হতে হবে।\n\n`queue_id` সুপারিশ করি: এক্সটেনশন ব্যস্ত বা অফলাইন থাকলে গ্রাহকের ফোন কেটে যায়, কিউতে হোল্ড মিউজিক ও failover আছে।","required":["on_digit"],"properties":{"on_digit":{"type":"string","example":"1"},"queue_id":{"type":"integer","nullable":true,"description":"ঠিক একটি গন্তব্য দিন"},"extension_id":{"type":"integer","nullable":true}}},"Call":{"type":"object","properties":{"id":{"type":"string","example":"call_1042"},"status":{"type":"string","enum":["queued","ringing","answered","busy","no_answer","failed","cancelled","expired"],"example":"answered"},"to":{"type":"string","example":"8801712345678","description":"সবসময় canonical রূপে ফেরত আসে"},"to_display":{"type":"string","example":"01712345678"},"reference":{"type":"string","nullable":true,"example":"order-8842"},"created_at":{"type":"string","format":"date-time"},"dialed_at":{"type":"string","format":"date-time","nullable":true},"ended_at":{"type":"string","format":"date-time","nullable":true},"digit":{"type":"string","nullable":true,"description":"গ্রাহক যা চেপেছেন। কিছু না চাপলে `null`। চাওয়া হয়নি এমন কী চাপলেও যা চেপেছেন সেটাই ফেরত আসে।"},"connected":{"type":"boolean","description":"এজেন্ট বা মেনুতে হস্তান্তর হয়েছিল কিনা"},"answered_sec":{"type":"integer"},"gather":{"type":"object","nullable":true,"properties":{"digits":{"type":"array","items":{"type":"string"}}}},"webhook":{"type":"object","nullable":true,"properties":{"url":{"type":"string"},"status":{"type":"string","enum":["pending","delivered","failed"]},"attempts":{"type":"integer"}}}}},"CallList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Call"}},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"},"last_page":{"type":"integer"}}}}},"AudioList":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer","example":12},"name":{"type":"string","example":"বকেয়া বার্তা"},"duration_sec":{"type":"integer","example":14},"created_at":{"type":"string","format":"date-time"}}}}}},"Error":{"type":"object","description":"প্রতিটি এরর এই একই আকারে আসে — একবার হ্যান্ডল করলেই চলে।","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthenticated","admin_key_forbidden","account_inactive","subscription_inactive","feature_unavailable","scope_required","validation_failed","not_found","not_cancellable","idempotency_conflict","idempotency_key_reused","wrong_host","wrong_account_host","rate_limited","too_many_attempts","request_failed"]},"message":{"type":"string","description":"বাংলায়, মানুষের পড়ার জন্য"},"fields":{"type":"object","nullable":true,"description":"শুধু `validation_failed`-এ: কোন ফিল্ডে কী সমস্যা"},"required_scope":{"type":"string","nullable":true,"description":"শুধু `scope_required`-এ: কোন অনুমতিটি লাগত"},"base_url":{"type":"string","nullable":true,"description":"শুধু `wrong_host`-এ: সঠিক ঠিকানা"}}}}}},"responses":{"Unauthorized":{"description":"কী নেই, ভুল, বা বাতিল করা — `unauthenticated`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"অনুমতি নেই — `scope_required`, `subscription_inactive`, `feature_unavailable`, `admin_key_forbidden`, `wrong_account_host`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"এই কলটি আপনার নয়, বা নেই — `not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"`not_cancellable` (কল শুরু হয়ে গেছে), `idempotency_conflict`, বা `idempotency_key_reused` (একই কী, অন্যরকম রিকোয়েস্ট)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ValidationFailed":{"description":"রিকোয়েস্টে সমস্যা — `validation_failed`, সাথে `fields`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"খুব দ্রুত অনেক রিকোয়েস্ট — `rate_limited`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}