ورود به پنل
آموزش API برای توسعه‌دهندگان

با API تیپینتا صورتحساب را از نرم‌افزار خودتان بفرستید

این صفحه یک آموزش گام‌به‌گام است: از گرفتن توکن تا ارسال اولین صورتحساب، پیگیری وضعیت و دریافت گزارش. فهرست کامل مسیرها و فیلدها در مرجع API است.

معرفی API

با API می‌توانید بدون ورود به پنل شماره‌های اقتصادی را مدیریت کنید، مانده‌ی اعتبار را بخوانید، صورتحساب را به‌صورت تکی یا گروهی ارسال کنید، وضعیتشان را استعلام بگیرید و گزارش ارسالی‌ها و خطاها را بخوانید. برای اتصال نرم‌افزار حسابداری، فروشگاه‌ساز یا هر سیستم داخلی شما مناسب است.

  • آدرس پایه: https://api.tpinta.com/v1 (فقط HTTPS)
  • قالب داده: JSON با کدگذاری UTF-8؛ نام فیلدها camelCase است و زمان‌ها UTC و ISO 8601 هستند.
  • مرجع کامل: api.tpinta.com/docs، به‌همراه کالکشن آماده‌ی Postman.

همه‌ی پاسخ‌ها یک قالب ثابت دارند:

{ "success": true, "data": { ... } }

{ "success": false, "error": { "code": "validation_failed", "message": "...", "field": "name" }, "traceId": "00-4bf9..." }

در برنامه‌ی خودتان error.code را بررسی کنید (ثابت است)؛ message فارسی است و برای نمایش به انسان. هنگام گزارش مشکل به پشتیبانی traceId را هم بفرستید.

پیش‌نیازها

  1. شماره اقتصادی. در پنل ثبت کنید (برای تمرین، محیط آزمایشی). راهنمای ثبت شماره اقتصادی
  2. اعتبار. هزینه‌ی هر صورتحساب از اعتبار حساب کم می‌شود. راهنمای افزایش اعتبار
  3. کلید API. از منوی «کلید API» پنل بسازید (با رمز پنل). کلید با ei_ شروع می‌شود، فقط یک‌بار نمایش داده می‌شود و هر حساب حداکثر ۵ کلید فعال دارد. راهنمای کلید API
مهم: API برای فراخوانی از سرور شما ساخته شده است. رمز پنل و کلید API را در برنامه‌ی موبایل، جاوااسکریپت سمت مرورگر یا مخزن Git نگذارید.

قدم اول: دریافت توکن

با سه مورد وارد می‌شوید: موبایل پنل، رمز پنل و کلید API. این سه فقط برای گرفتن توکن لازم‌اند؛ بعد از آن در همه‌ی درخواست‌ها فقط توکن می‌فرستید.

POST/v1/auth/token

بدون نیاز به توکن

curl -X POST https://api.tpinta.com/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{"mobile":"09120000000","password":"رمز-پنل","apiKey":"ei_..."}'
{
  "success": true,
  "data": { "accessToken": "CfDJ8...", "tokenType": "Bearer", "expiresIn": 3600 }
}
  • توکن را در هدر Authorization: Bearer ACCESS_TOKEN همه‌ی درخواست‌های بعدی بفرستید.
  • اعتبار توکن پیش‌فرض ۶۰ دقیقه است (expiresIn بر حسب ثانیه). برای هر درخواست توکن جدید نگیرید؛ توکن را نگه دارید و تا پایان اعتبارش استفاده کنید.
  • تغییر رمز پنل همه‌ی توکن‌های قبلی را باطل می‌کند و لغو کلید در پنل توکن‌های آن را حداکثر ظرف حدود ۳۰ ثانیه از کار می‌اندازد.
  • ۵ بار تلاش ناموفق برای یک کلید در ۱۵ دقیقه، آن کلید را موقتاً مسدود می‌کند (429 too_many_attempts). این مسدودسازی ورود به پنل را تحت‌تأثیر قرار نمی‌دهد.

قدم دوم: تست اتصال و گرفتن شناسه‌ها

قبل از ارسال صورتحساب، این سه درخواست ساده را بزنید:

curl https://api.tpinta.com/v1/me              -H "Authorization: Bearer ACCESS_TOKEN"
curl https://api.tpinta.com/v1/credit/balance   -H "Authorization: Bearer ACCESS_TOKEN"
curl https://api.tpinta.com/v1/economic-ids     -H "Authorization: Bearer ACCESS_TOKEN"
  • /v1/me: سالم بودن توکن را نشان می‌دهد.
  • /v1/credit/balance: مانده‌ی اعتبار، هزینه‌ی هر صورتحساب و تعداد صورتحساب قابل ارسال.
  • /v1/economic-ids: فهرست شماره‌های اقتصادی شما.
{ "success": true, "data": { "balance": 1500000, "invoiceFee": 3000, "remainingInvoices": 500 } }
{
  "success": true,
  "data": { "items": [ { "id": 12, "code": "10101234567", "isTest": true, "active": true, "...": "..." } ] }
}
به این نکته دقت کنید: در ارسال صورتحساب، فیلد economicId همان id در این فهرست است (مثلاً ۱۲)، نه خود کد اقتصادی (مثلاً ۱۰۱۰۱۲۳۴۵۶۷).

اگر هنوز شماره‌ای ثبت نکرده‌اید، items خالی است. شماره‌ی اقتصادی را از پنل یا با POST /v1/economic-ids ثبت کنید (حذف شماره فقط از پنل ممکن است).

قدم سوم: ارسال اولین صورتحساب

POST/v1/invoices

سقف: ۶۰ درخواست در دقیقه برای هر توکن

POST https://api.tpinta.com/v1/invoices
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "clientRef": "INV-2026-000123",
  "economicId": 12,
  "subject": 1,
  "pattern": 1,
  "type": 1,
  "buyerType": 2,
  "buyer": { "tinb": "10101234567" },
  "headerExtra": { "setm": 1 },
  "items": [
    { "sstid": "1234567890123", "sstt": "خدمات نمونه", "am": 2, "fee": 1000000, "vra": 10 }
  ]
}
{
  "success": true,
  "data": { "taxId": "A12B34C0000000123456XY", "clientRef": "INV-2026-000123", "status": "queued", "duplicate": false }
}

فیلدهای اصلی

فیلدتوضیح
clientRefشناسه‌ی یکتای درخواست (۸ تا ۶۴ نویسه؛ حروف انگلیسی، عدد و . _ : -). مثلاً شماره‌ی فاکتور خودتان. در بخش «جلوگیری از ارسال تکراری» توضیح داده شده.
economicIdشناسه‌ی شماره اقتصادی صادرکننده (فیلد id در فهرست شماره‌ها).
subjectموضوع: ۱ اصلی · ۲ اصلاحی · ۳ ابطالی · ۴ برگشت از فروش.
pattern · typeالگو (مثلاً ۱ فروش) و نوع صورتحساب (۱ نوع اول · ۲ نوع دوم · ۳ نوع سوم).
buyerType · buyerنوع شخص خریدار (۱ حقیقی · ۲ حقوقی · ۳ مشارکت مدنی · ۴ اتباع غیرایرانی) و اطلاعات او: tinb شماره اقتصادی، bid شناسه/کد ملی، bpc کد پستی. اطلاعات لازم به الگو و نوع خریدار بستگی دارد و پیام خطا دقیق می‌گوید.
headerExtraفیلدهای سربرگ مخصوص الگو؛ مثلاً setm روش تسویه (۱ نقدی · ۲ نسیه · ۳ نقدی/نسیه) که در نوع اول لازم است.
itemsاقلام (حداکثر ۵۰۰ ردیف): sstid شناسه‌ی ۱۳ رقمی کالا/خدمت، sstt شرح، am تعداد، fee مبلغ واحد (ریال)، dis تخفیف، vra نرخ مالیات بر ارزش افزوده (درصد).
issuedAtاختیاری؛ زمان صدور به‌صورت ISO 8601. خالی یعنی همین الان و زمان آینده پذیرفته نمی‌شود.
paymentاختیاری؛ اطلاعات پرداخت (برای نوع سوم لازم است).
ackAmountWarningاختیاری؛ تأیید هشدار «مبلغ غیرعادی» (پایین‌تر).

مبالغ محاسباتی هر ردیف (مثل prdis، adis، vam، tsstam) را لازم نیست خودتان حساب کنید؛ سرور با همان فرمول پنل حساب می‌کند. اگر خودتان بفرستید، مقدار شما راستی‌آزمایی می‌شود و در صورت اشتباه خطای دقیق می‌گیرید. شناسه‌ی کالا/خدمت را از جستجوی شناسه کالا و خدمت پیدا کنید.

201 یعنی «ثبت و در صف قرار گرفت»، نه اینکه سامانه مودیان آن را تأیید کرده باشد. صورتحساب خودکار و بعداً ارسال می‌شود؛ taxId (۲۲ نویسه) را برای استعلام و گزارش نگه دارید.

اگر صورتحساب رد شد

اعتبارسنجی سرور همان قواعد پنل را دارد. اگر مشکلی باشد هیچ‌چیز ثبت نمی‌شود و اعتباری کم نمی‌شود و همه‌ی خطاها یک‌جا در details می‌آید:

{
  "success": false,
  "error": {
    "code": "invoice_invalid",
    "message": "اعتبارسنجی صورتحساب ناموفق بود",
    "details": [ "ردیف 1: شناسه کالا/خدمت در فهرست یافت نشد", "..." ]
  },
  "traceId": "00-4bf92f35..."
}
پاسخمعنی و کار درست
400 validation_failedفیلدی در بدنه ناقص یا نامعتبر است؛ نام آن در field می‌آید.
422 invoice_invalidقواعد صورتحساب رعایت نشده؛ خطاهای details را اصلاح و دوباره بفرستید (همان clientRef قابل استفاده است).
402 insufficient_creditاعتبار کافی نیست؛ از پنل شارژ کنید.
409 amount_warningمبلغ نسبت به سابقه‌ی همین شماره اقتصادی یا سقف تعیین‌شده در پنل غیرعادی است. اگر مبلغ درست است، همان درخواست را با "ackAmountWarning": true دوباره بفرستید.
404 not_foundشماره اقتصادی پیدا نشد یا متعلق به حساب شما نیست.

قدم چهارم: پیگیری وضعیت

وضعیت نهایی را با استعلام می‌گیرید. وضعیت از دیتابیس تیپینتا خوانده می‌شود و ارسال خودکار آن را به‌روز می‌کند؛ پس بعد از ثبت، چند ثانیه تا چند دقیقه طول می‌کشد تا queued به success یا failed برسد.

GET/v1/invoices/{{taxId}}

GET/v1/invoices/by-ref/{{clientRef}}

وقتی پاسخ ارسال به دست شما نرسیده و taxId ندارید

curl https://api.tpinta.com/v1/invoices/A12B34C0000000123456XY      -H "Authorization: Bearer ACCESS_TOKEN"
curl https://api.tpinta.com/v1/invoices/by-ref/INV-2026-000123      -H "Authorization: Bearer ACCESS_TOKEN"
{ "success": true, "data": { "taxId": "A12B34C0000000123456XY", "status": "success", "message": null, "referenceNumber": "1234567890123456" } }
statusمعنی
pendingفقط در استعلام با clientRef: شناسه رزرو شده ولی صورتحساب هنوز ثبت نشده است (حدود یک دقیقه بعد همان درخواست را دوباره بفرستید).
queuedثبت شده و در صف ارسال است.
processingدر حال ارسال یا پیگیری در سامانه مودیان.
successسامانه مودیان تأیید کرده؛ referenceNumber شماره‌ی پیگیری است.
failedرد یا ناموفق؛ علت در message آمده است.

هر ۱۰ تا ۳۰ ثانیه یک‌بار استعلام کنید، نه پشت‌سرهم. برای وضعیت چند صورتحساب هم‌زمان از استعلام گروهی (POST /v1/invoices/status، حداکثر ۱۰۰ مورد) استفاده کنید.

جلوگیری از ارسال تکراری با clientRef

اگر درخواست ارسال را بفرستید ولی به‌خاطر قطعی شبکه یا timeout پاسخی نگیرید، نمی‌دانید صورتحساب ثبت شده یا نه. ارسال دوباره‌ی بی‌احتیاط می‌تواند دو صورتحساب بسازد و دو بار اعتبار کم کند. برای همین هر ارسال یک clientRef یکتا دارد:

حالتنتیجه
اولین ارسال201 و صورتحساب ثبت می‌شود.
تکرار با همان clientRef و همان محتوا200 با duplicate: true و هدر Idempotent-Replayed: true؛ صورتحساب تازه‌ای ساخته نمی‌شود و همان taxId و وضعیت فعلی برمی‌گردد.
همان clientRef با محتوای دیگر409 idempotency_conflict
درخواست قبلی هنوز در جریان است409 request_in_progress با هدر Retry-After؛ کمی بعد دوباره بفرستید.
ارسال قبلی رد شده (مثلاً 422)چیزی ثبت نشده؛ همان clientRef را با محتوای اصلاح‌شده دوباره استفاده کنید.
قاعده‌ی طلایی: هنگام timeout یا خطای شبکه، همان درخواست را با همان clientRef و همان بدنه دوباره بفرستید یا با GET /v1/invoices/by-ref/{clientRef} بپرسید. فقط وقتی مطمئنید صورتحساب قبلی ثبت نشده، clientRef جدید بسازید.

ارسال گروهی

برای چند صورتحساب در یک درخواست (حداکثر ۵۰ مورد، بدنه تا ۵ مگابایت). هر مورد دقیقاً مثل ارسال تکی پردازش می‌شود و شکست یک مورد بقیه را متوقف نمی‌کند. سقف: ۱۰ درخواست در دقیقه برای هر توکن.

POST/v1/invoices/batch

POST https://api.tpinta.com/v1/invoices/batch

{
  "invoices": [
    { "clientRef": "INV-2026-000201", "economicId": 12, "subject": 1, "pattern": 1, "type": 1, "buyerType": 2,
      "buyer": { "tinb": "10101234567" }, "headerExtra": { "setm": 1 }, "items": [ { "...": "..." } ] },
    { "clientRef": "INV-2026-000202", "...": "..." }
  ]
}
{
  "success": true,
  "data": {
    "summary": { "total": 3, "accepted": 1, "replayed": 1, "failed": 1 },
    "results": [
      { "index": 0, "clientRef": "INV-2026-000201", "ok": true,  "taxId": "A12B...XY", "status": "queued",  "duplicate": false },
      { "index": 1, "clientRef": "INV-2026-000202", "ok": true,  "taxId": "A12B...XZ", "status": "success", "duplicate": true  },
      { "index": 2, "clientRef": "INV-2026-000203", "ok": false, "error": { "code": "invoice_invalid", "message": "...", "details": [ "..." ] } }
    ]
  }
}
  • کد 200 یعنی «درخواست پردازش شد»، نه «همه ثبت شدند». موفقیت هر مورد را در results[i].ok بررسی کنید.
  • ترتیب results با ترتیب invoices یکی است. duplicate: true یعنی قبلاً با همان clientRef ثبت شده بود.
  • اگر اعتبار وسط دسته تمام شود، موارد اول ثبت و بعدی‌ها با 402 رد می‌شوند؛ قبل از دسته‌های بزرگ مانده‌ی اعتبار را بخوانید.
  • پردازش پشت‌سرهم است. timeout سمت خودتان را حداقل ۶۰ ثانیه بگذارید و برای شروع دسته‌های ۱۰ تا ۲۰ تایی را امتحان کنید.
  • اگر پاسخ کل درخواست به دست شما نرسید، همه‌ی clientRefها را با استعلام گروهی بررسی کنید:
POST https://api.tpinta.com/v1/invoices/status

{ "clientRefs": [ "INV-2026-000201", "INV-2026-000202" ] }

اصلاحی، ابطالی و برگشت از فروش

صورتحساب ارسال‌شده ویرایش نمی‌شود؛ سند جدید با همان مسیر POST /v1/invoices می‌فرستید و در آن subject و referenceTaxId (شماره مالیاتی صورتحساب مرجع) را می‌گذارید.

  • subject: 2: اصلاحی.
  • subject: 3: ابطالی؛ الگو، نوع، خریدار و اقلام از صورتحساب مرجع گرفته می‌شود و فقط referenceTaxId لازم است.
  • subject: 4: برگشت از فروش؛ در اقلام «مانده‌ی فروش پس از برگشت» را بفرستید، نه تعداد برگشتی را.
{
  "clientRef": "RET-2026-000045",
  "economicId": 12,
  "subject": 4,
  "referenceTaxId": "A12B34C0000000123456XY",
  "pattern": 1, "type": 1, "buyerType": 2,
  "buyer": { "tinb": "10101234567" },
  "headerExtra": { "setm": 1 },
  "items": [ { "sstid": "1234567890123", "sstt": "خدمات نمونه", "am": 1, "fee": 1000000, "vra": 10 } ]
}

مرجع باید با موفقیت ثبت شده و متعلق به حساب شما باشد.

گزارش‌ها

دو گزارش فقط‌خواندنی دارید که فقط داده‌ی حساب صاحب توکن را برمی‌گردانند:

GET https://api.tpinta.com/v1/reports/invoices?from=2026-10-01&to=2026-11-01&status=failed&size=25&page=1
GET https://api.tpinta.com/v1/reports/errors?from=2026-10-01&to=2026-11-01&source=submit
  • /v1/reports/invoices: همه‌ی صورتحساب‌ها با هر کانالی (ویزارد، اکسل یا API). فیلترها: status، economicId، subject، pattern، channel (۱ ویزارد · ۲ اکسل · ۳ API)، q و sort.
  • /v1/reports/errors: صورتحساب‌های ردشده با علت. source: validation (کنترل‌های سامانه پیش از ارسال)، submit (رد هنگام ارسال) یا inquiry (رد در استعلام نهایی).
  • بازه‌ی تاریخ بر اساس تاریخ صدور است (میلادی و UTC). from شامل خودش و to بدون خودش است. اگر نفرستید ۳۰ روز اخیر برمی‌گردد و طول بازه حداکثر ۹۲ روز است.
  • صفحه‌بندی: page از ۱ و size بین ۱ تا ۱۰۰ (پیش‌فرض ۱۰). پاسخ total و items دارد؛ تعداد صفحه‌ها ceil(total / size) است.

خطاها و محدودیت‌ها: چطور واکنش نشان دهیم

پاسخکار درست
401 unauthorizedیک‌بار توکن جدید بگیرید و همان درخواست را دوباره بفرستید. اگر باز 401 گرفتید، کلید لغو شده یا رمز پنل عوض شده است؛ بی‌نهایت تلاش نکنید.
429 rate_limitedتا مقدار هدر Retry-After (اگر بود) صبر کنید و دوباره بفرستید. تعداد درخواست‌ها را کم کنید یا از ارسال گروهی استفاده کنید.
5xx، 503 یا timeoutبا فاصله‌ی رو به افزایش (مثلاً ۱، ۲، ۴ ثانیه و حداکثر چند بار) دوباره بفرستید. در ارسال صورتحساب همان clientRef را بفرستید.
سایر 4xxتکرار بی‌فایده است؛ error.message و field را بخوانید و درخواست را اصلاح کنید.

سقف نرخ (پیش‌فرض)

محدودهسقف
دریافت توکن۲۰ درخواست در ۱۰ دقیقه برای هر IP
درخواست‌های خواندنی۱۲۰ درخواست در دقیقه برای هر توکن
ارسال صورتحساب۶۰ درخواست در دقیقه برای هر توکن
ارسال گروهی۱۰ درخواست در دقیقه برای هر توکن
ثبت و ویرایش شماره اقتصادی۳۰ درخواست در ۱۰ دقیقه برای هر توکن
کل درخواست‌ها۶۰۰ درخواست در دقیقه برای هر IP

سقف‌ها ممکن است در آینده تغییر کنند؛ برنامه‌ی شما باید با 429 کمی صبر کند و دوباره تلاش کند. فهرست کامل کدهای خطا در مرجع API است.

نکات امنیتی

  • رمز و کلید API را در متغیر محیطی یا گنجینه‌ی اسرار سرور نگه دارید، نه در کد یا Git. بدنه‌ی درخواست دریافت توکن و هدر Authorization را در لاگ‌های خودتان ننویسید.
  • API را مستقیم از مرورگر یا اپ موبایل صدا نزنید. CORS باز نیست و رمز و کلید نباید دست کاربر نهایی باشد.
  • چرخش کلید: در پنل کلید جدید بسازید، برنامه‌ی خود را روی آن ببرید و بعد کلید قدیمی را لغو کنید.
  • اعتبار گواهی TLS سرور را در کلاینت خود غیرفعال نکنید.
  • برای رهگیری و امنیت، از هر درخواست فقط متد، مسیر (بدون query)، کد پاسخ، مدت پردازش، IP، traceId و clientRef نگه‌داری می‌شود. بدنه‌ی درخواست، توکن، رمز و کلید ذخیره نمی‌شوند.

نمونه‌ی کامل: توکن، ارسال و پیگیری

این نمونه‌ها فقط برای فهم جریان کار هستند. در برنامه‌ی واقعی توکن را نگه دارید و دوباره استفاده کنید، خطاها را طبق جدول بالا مدیریت کنید و رمز و کلید را از تنظیمات امن سرور بخوانید.

Python
import time
import requests

BASE = "https://api.tpinta.com/v1"

# ۱) توکن (در برنامه‌ی واقعی تا پایان اعتبارش نگه دارید و دوباره استفاده کنید)
r = requests.post(f"{BASE}/auth/token", timeout=30, json={
    "mobile": "09120000000", "password": "رمز-پنل", "apiKey": "ei_..."})
r.raise_for_status()
H = {"Authorization": "Bearer " + r.json()["data"]["accessToken"]}

# ۲) ارسال صورتحساب. clientRef را از شماره‌ی فاکتور خودتان بسازید
invoice = {
    "clientRef": "INV-2026-000123",
    "economicId": 12,
    "subject": 1, "pattern": 1, "type": 1, "buyerType": 2,
    "buyer": {"tinb": "10101234567"},
    "headerExtra": {"setm": 1},
    "items": [{"sstid": "1234567890123", "sstt": "خدمات نمونه", "am": 2, "fee": 1000000, "vra": 10}],
}
try:
    res = requests.post(f"{BASE}/invoices", json=invoice, headers=H, timeout=30)
except requests.Timeout:
    # نمی‌دانیم ثبت شده یا نه: همین بدنه را با همین clientRef دوباره بفرستید (یا استعلام با clientRef)
    raise

body = res.json()
if not body["success"]:
    err = body["error"]
    print(err["code"], err["message"], err.get("details"))
    raise SystemExit(1)

tax_id = body["data"]["taxId"]

# ۳) پیگیری وضعیت نهایی: هر ۳۰ ثانیه، حداکثر حدود ۱۰ دقیقه
for _ in range(20):
    time.sleep(30)
    s = requests.get(f"{BASE}/invoices/{tax_id}", headers=H, timeout=30).json()["data"]
    if s["status"] in ("success", "failed"):
        print(s["status"], s.get("referenceNumber"), s.get("message"))
        break
C#
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;

var http = new HttpClient { BaseAddress = new Uri("https://api.tpinta.com/v1/") };

// ۱) توکن
var tokenRes = await http.PostAsJsonAsync("auth/token",
    new { mobile = "09120000000", password = "رمز-پنل", apiKey = "ei_..." });
var tokenDoc = await tokenRes.Content.ReadFromJsonAsync<JsonElement>();
var token = tokenDoc.GetProperty("data").GetProperty("accessToken").GetString();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

// ۲) ارسال صورتحساب
var invoice = new
{
    clientRef = "INV-2026-000123",
    economicId = 12, subject = 1, pattern = 1, type = 1, buyerType = 2,
    buyer = new { tinb = "10101234567" },
    headerExtra = new { setm = 1 },
    items = new[] { new { sstid = "1234567890123", sstt = "خدمات نمونه", am = 2, fee = 1000000, vra = 10 } }
};
var res = await http.PostAsJsonAsync("invoices", invoice);
var body = await res.Content.ReadFromJsonAsync<JsonElement>();
if (!body.GetProperty("success").GetBoolean()) { Console.WriteLine(body); return; }

// ۳) پیگیری وضعیت
var taxId = body.GetProperty("data").GetProperty("taxId").GetString();
for (var i = 0; i < 20; i++)
{
    await Task.Delay(TimeSpan.FromSeconds(30));
    var s = await http.GetFromJsonAsync<JsonElement>($"invoices/{taxId}");
    var status = s.GetProperty("data").GetProperty("status").GetString();
    if (status is "success" or "failed") { Console.WriteLine(s); break; }
}
JavaScript (Node.js)
const BASE = 'https://api.tpinta.com/v1';   // فقط روی سرور خودتان (Node.js)، نه در مرورگر

const t = await fetch(BASE + '/auth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ mobile: '09120000000', password: 'رمز-پنل', apiKey: 'ei_...' })
});
const { data } = await t.json();
const H = { Authorization: 'Bearer ' + data.accessToken, 'Content-Type': 'application/json' };

const res = await fetch(BASE + '/invoices', {
  method: 'POST', headers: H,
  body: JSON.stringify({
    clientRef: 'INV-2026-000123', economicId: 12, subject: 1, pattern: 1, type: 1, buyerType: 2,
    buyer: { tinb: '10101234567' }, headerExtra: { setm: 1 },
    items: [{ sstid: '1234567890123', sstt: 'خدمات نمونه', am: 2, fee: 1000000, vra: 10 }]
  })
});
console.log(await res.json());

برای امتحان سریع بدون کدنویسی، کالکشن Postman را بگیرید، متغیرهای mobile، password و apiKey را پر کنید و «دریافت توکن» را اجرا کنید؛ توکن خودکار ذخیره می‌شود.

سؤالات متداول

برای شروع به چه چیزهایی نیاز دارم؟

یک شماره اقتصادی ثبت‌شده (پیشنهاد می‌شود اول آزمایشی)، مقداری اعتبار در پنل و یک کلید API که از منوی «کلید API» پنل می‌سازید. با موبایل و رمز پنل و کلید API توکن می‌گیرید.

چرا بعد از ارسال، وضعیت success نمی‌گیرم؟

پاسخ 201 یعنی صورتحساب ثبت و در صف ارسال قرار گرفته است. ارسال به سامانه مودیان و تأیید بعداً و خودکار انجام می‌شود؛ وضعیت نهایی را با استعلام بگیرید و هر ۱۰ تا ۳۰ ثانیه یک‌بار بپرسید.

اگر هنگام ارسال timeout شد چه کنم؟

همان درخواست را با همان clientRef و همان بدنه دوباره بفرستید، یا وضعیت را با GET /v1/invoices/by-ref/{clientRef} بپرسید. با clientRef جدید دوباره نفرستید، چون ممکن است دو صورتحساب ساخته شود.

آیا می‌توانم API را از مرورگر یا اپ موبایل صدا بزنم؟

خیر. رمز پنل و کلید API نباید دست کاربر نهایی باشد و CORS هم باز نیست. API را فقط از سرور خودتان صدا بزنید.

توکن تا کی اعتبار دارد؟

به‌طور پیش‌فرض ۶۰ دقیقه؛ مقدار دقیق در فیلد expiresIn (ثانیه) می‌آید. با خطای 401 یک‌بار توکن جدید بگیرید و همان درخواست را دوباره بفرستید.

مستندات کامل و کالکشن Postman کجاست؟

مرجع کامل همه‌ی مسیرها و فیلدها در api.tpinta.com/docs است و کالکشن آماده‌ی Postman را هم از همان صفحه می‌توانید بگیرید.

آماده‌اید وصل شوید؟

کلید API بسازید، توکن بگیرید و اولین صورتحساب را در محیط آزمایشی بفرستید.

تیپینتا سامانه‌ی رسمی سازمان امور مالیاتی نیست. مبالغ و شناسه‌های داخل نمونه‌ها فرضی‌اند؛ مقادیر واقعی خودتان را بگذارید.