راهنمای فنی API نسخه ۲ آسا

ثبت مشتری، ثبت گروهی صورتحساب، اعتبارسنجی JSON و پیگیری وضعیت

Overview

قرارداد عمومی API

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

Base URL https://api-v2.asatsp.ir
Content-Type application/json; charset=utf-8
Authentication Authorization: Bearer <AccessToken>
Batch Size 1 تا 250 صورتحساب
ثبت چند صورتحساب در یک درخواست پاسخ خطای ساخت‌یافته با errors[] پردازش مستقل و پاسخ ۱:۱ برای هر عضو عدم نمایش stack trace در پاسخ عمومی
Recommended Flow

جریان کاری پیشنهادی

برای پیاده‌سازی پایدار، مدیر هر کسب‌وکار باید client_id/client_secret را از بخش «اتصال API v2» در تنظیمات همان کسب‌وکار دریافت کند و سپس Access Token کوتاه‌مدت بگیرد. در ثبت مشتری، خروجی علاوه بر customerApiKey قدیمی، اطلاعات serviceAuth جدید را هم برمی‌گرداند.

ثبت مشتری /api/register مشتری را ایجاد یا به‌روزرسانی می‌کند و Customer API Key legacy و serviceAuth جدید را بازمی‌گرداند.
ساخت درخواست گروهی uuid + data[] هر عضو data دارای internalId یکتا است و مستقل از سایر اعضا اعتبارسنجی و ذخیره می‌شود؛ سقف هر درخواست ۲۵۰ صورتحساب است.
ثبت صورتحساب /api/invoice/send قبل از ذخیره، JSON و جمع‌های مالی کنترل می‌شوند.
پیگیری وضعیت /api/invoice/inquiry* وضعیت نهایی با internalId، no یا taxId پیگیری می‌شود.
ثبت مشتری و دریافت کلید دسترسی سرویس /api/register را با Access Token دارای scope v2.register فراخوانی کنید. در دوره مهاجرت، Partner API Key قدیمی هم پذیرفته می‌شود.
ساخت JSON صورتحساب‌ها در هر درخواست، آرایه data می‌تواند ۱ تا ۲۵۰ صورتحساب داشته باشد. هر عضو باید internalId یکتا، Header، Body و در صورت نیاز Payments داشته باشد.
ثبت گروهی و دریافت پاسخ آرایه پاسخ data به همان ترتیب ورودی، نتیجه موفق یا خطای هر صورتحساب را برمی‌گرداند. خطای یک عضو مانع ثبت اعضای سالم نمی‌شود.
پیگیری وضعیت صورتحساب با internalId، inno یا taxId آخرین وضعیت پردازش را دریافت کنید.
Authentication

احراز هویت

روش اصلی احراز هویت API نسخه ۲ از این پس الگوی client_credentials است. کلیدهای ثابت قدیمی فقط برای دوره مهاجرت پذیرفته می‌شوند.

محل دریافت Client ID و Client Secret: وارد پنل کنترل معتمد آسا شوید، از منوی «کسب‌وکارها» کسب‌وکار موردنظر را انتخاب کنید و در «تنظیمات ← اتصال API v2» اعتبارنامه را صادر کنید. مسیر مستقیم هر کسب‌وکار نیز /apps/taxpayer-management/setting/{companyId}/api-v2 است. مقدار Client Secret فقط هنگام صدور یا چرخش نمایش داده می‌شود؛ همان لحظه آن را در Secret Manager سرور ذخیره کنید.
HTTP Header
Authorization: Bearer <AccessToken>
Content-Type: application/json; charset=utf-8
روشمحل استفادهتوضیح
Access Tokenهمه endpointهاتوکن کوتاه‌مدت JWT که با client_id/client_secret گرفته می‌شود و بر اساس scope کنترل می‌شود.
Partner API Key legacy/api/registerفقط تا تاریخ 2026-10-08T00:00:00Z برای فرصت مهاجرت فعال است.
Customer API Key legacy/api/invoice/*فقط تا تاریخ 2026-10-08T00:00:00Z برای فرصت مهاجرت فعال است.
اگر اتصال شما هنوز با Partner API Key یا Customer API Key انجام می‌شود، تا تاریخ 2026-10-08T00:00:00Z قطع نمی‌شود. پاسخ‌های legacy هدرهای Deprecation، Sunset و Warning دارند. پیاده‌سازی جدید باید از Access Token استفاده کند.

دریافت یا چرخش client credentials

روش اصلی این است که مدیر کسب‌وکار از «پنل کنترل معتمد آسا ← کسب‌وکارها ← تنظیمات ← اتصال API v2» اعتبارنامه را صادر یا Client Secret را بچرخاند. Client ID همیشه در همان صفحه قابل مشاهده است، اما client_secret فقط یک‌بار نمایش داده می‌شود؛ آن را در secret store نگه‌داری کنید و در کد، مرورگر یا لاگ ثبت نکنید.

مسیر موقت مهاجرت برای اتصال‌های قدیمی

اگر یک اتصال قدیمی از قبل API key معتبر دارد، تا پایان مهلت مهاجرت می‌تواند یک بار از endpoint زیر برای ساخت credential جدید استفاده کند. برای راه‌اندازی‌های جدید از پنل کنترل استفاده کنید.

POST /api/auth/v2/client-credentials
curl -X POST "https://api-v2.asatsp.ir/api/auth/v2/client-credentials" \
  -H "Authorization: Bearer <PartnerApiKey | CustomerApiKey>" \
  -H "Content-Type: application/json; charset=utf-8" \
  -d '{
    "credential_type": "customer",
    "rotate_secret": false
  }'

دریافت Access Token

توکن‌ها کوتاه‌مدت هستند و مقدار پیش‌فرض انقضا ۶۰ دقیقه است. اگر scope ارسال نشود، همه scopeهای مجاز همان client استفاده می‌شود.

POST /api/auth/v2/token
curl -X POST "https://api-v2.asatsp.ir/api/auth/v2/token" \
  -H "Content-Type: application/json; charset=utf-8" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "v2-customer-12345",
    "client_secret": "<client_secret>",
    "scope": "v2.invoice.send v2.invoice.read"
  }'
Scopeکاربرد
v2.registerثبت یا به‌روزرسانی مشتری توسط پارتنر.
v2.invoice.sendثبت صورتحساب و ارسال دستی صورتحساب‌های ثبت‌شده.
v2.invoice.cancelابطال صورتحساب.
v2.invoice.readاستعلام با internalId، شماره یا taxId.
v2.lookup.readخواندن لیست واحدها و ارزها.
بعد از تاریخ 2026-10-08T00:00:00Z، API key ثابت در v2 پذیرفته نمی‌شود و فقط Access Token معتبر با scope لازم قابل استفاده است.
Endpoint Catalog

فهرست Endpointها

ثبت یا به‌روزرسانی مشتریPOST
/api/register ایجاد یا به‌روزرسانی مشتری و دریافت serviceAuth جدید همراه Customer API Key legacy.
ثبت با JSON استاندارد سازمانPOST
/api/invoice/send ثبت صورتحساب با ساختار رسمی سازمان امور مالیاتی در آرایه data.
فروش نوع اول با اطلاعات خریدارPOST
/api/invoice/salesWithBuyerData مدل ساده آرایه‌ای برای صورتحساب نوع اول، الگوی فروش عادی.
فروش نوع دوم به مصرف‌کننده نهاییPOST
/api/invoice/salesEndUser مدل ساده آرایه‌ای برای صورتحساب نوع دوم بدون الزام اطلاعات خریدار.
الگوهای تخصصی صورتحسابPOST
type 1: all / type 2: 1,3,9,13 پشتیبانی از الگوهای ارز، طلا، پیمانکاری، قبوض، پرواز، صادرات، بارنامه، فرآورده‌های نفتی، اوراق مبتنی بر کالا، بیمه و فروش زنجیره.
ارسال دستی و ثبت ابطالPOST
/api/invoice/sendInvoice قرار دادن صورتحساب‌های ثبت‌شده در صف ارسال و ثبت صورتحساب ابطالی.
داده‌های مرجع واحد و ارزGET
/api/InvoiceItemUnit و /api/Currency دریافت کدهای مرجع مورد استفاده در unit و currencyCode.
پیگیری با کد داخلیPOST
/api/invoice/inquiryInternalId دریافت وضعیت صورتحساب بر اساس internalId ارسالی.
پیگیری با شماره صورتحسابPOST
/api/invoice/inquiryNo دریافت وضعیت صورتحساب بر اساس شماره داخلی یا inno.
پیگیری با شماره مالیاتیPOST
/api/invoice/inquiryTaxId دریافت وضعیت صورتحساب بر اساس شماره منحصر به فرد مالیاتی.
وضعیت اعلامیه فروش بورس (الگوی ۱۱)POST
/api/invoice/inquiryExchangeAnnouncement و /api/invoice/exchangeAnnouncement استعلام و ثبت دستی تأیید اعلامیه فروش بورس زنجیره صورتحساب؛ پس از تأیید، فقط ابطال مجاز است.
ثبت پرداخت صورتحساب ارسال‌شده در کارپوشهPOST
/api/invoice/registerPayment و /api/invoice/inquiryPayment افزودن پرداخت‌های بعدی به صورتحساب نسیه یا نقدی/نسیهٔ ارسال‌شده و ثبت خودکار آن‌ها در سامانه مؤدیان؛ پیگیری وضعیت هر ردیف.
کارپوشه مالیاتی و عملیات ExcelGET / POST / PUT
/api/v2/tax-workspace/* خروجی، Preview و اجرای گروهی واکنش صورتحساب‌ها و قراردادهای شخص ثالث در Context کسب‌وکار احرازشده.
Customer Registration

ثبت یا به‌روزرسانی مشتری

POST /api/register

این سرویس با Access Token دارای scope v2.register فراخوانی می‌شود و پس از اعتبارسنجی شناسه حافظه مالیاتی، مشتری را ایجاد یا به‌روزرسانی می‌کند. در دوره مهاجرت، Partner API Key قدیمی هم پذیرفته می‌شود.

فیلدنوعالزامیتوضیح
typenumberبله1 حقیقی، 2 حقوقی، 3 مشارکت مدنی، 4 اتباع غیر ایرانی.
fiscalIdstring(6)بلهشناسه حافظه مالیاتی مشتری که باید در همان محیط درخواست اخذ و قابل استعلام باشد.
sandBoxbooleanخیرtrue برای ثبت و استعلام در Sandbox؛ مقدار پیش‌فرض false و به معنی محیط عملیاتی است.
economicCodestringبلهشماره اقتصادی مشتری؛ با شناسه حافظه مالیاتی کنترل می‌شود.
namestringخیرنام شخص یا شرکت.
nationalCodestringخیرکد ملی یا شناسه ملی.
mobilestringخیرشماره تلفن همراه.
autoSendbooleanخیردر صورت عدم ارسال مقدار، پیش‌فرض true اعمال می‌شود.
valueAddedNotCalledbooleanخیرمعادل گزینه «مشمول فراخوان مالیات بر ارزش افزوده نیست» در پنل است. با مقدار true، نرخ و مبلغ مالیات بر ارزش افزوده صورتحساب‌ها صفر کنترل می‌شود. در به‌روزرسانی مشتری، ارسال‌نشدن این فیلد مقدار فعلی را تغییر نمی‌دهد.
Registration Examples

مثال‌های چندزبانه ثبت مشتری

cURL / register
curl -X POST "https://api-v2.asatsp.ir/api/register" \
  -H "Authorization: Bearer <AccessToken with v2.register>" \
  -H "Content-Type: application/json; charset=utf-8" \
  -d '{
    "type": 2,
    "fiscalId": "A1B2C3",
    "economicCode": "12345678901234",
    "name": "شرکت نمونه",
    "nationalCode": "10100000000",
    "mobile": "09120000000",
    "autoSend": true,
    "valueAddedNotCalled": true
  }'
JavaScript fetch / register
const response = await fetch("https://api-v2.asatsp.ir/api/register", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <AccessToken with v2.register>",
    "Content-Type": "application/json; charset=utf-8"
  },
  body: JSON.stringify({
    type: 2,
    fiscalId: "A1B2C3",
    economicCode: "12345678901234",
    name: "شرکت نمونه",
    nationalCode: "10100000000",
    mobile: "09120000000",
    autoSend: true,
    valueAddedNotCalled: true
  })
});

const result = await response.json();
C# HttpClient / register
using System.Net.Http;
using System.Text;

var json = """
{
  "type": 2,
  "fiscalId": "A1B2C3",
  "economicCode": "12345678901234",
  "name": "شرکت نمونه",
  "nationalCode": "10100000000",
  "mobile": "09120000000",
  "autoSend": true,
  "valueAddedNotCalled": true
}
""";

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer <AccessToken with v2.register>");

var response = await client.PostAsync(
    "https://api-v2.asatsp.ir/api/register",
    new StringContent(json, Encoding.UTF8, "application/json"));

var body = await response.Content.ReadAsStringAsync();
Python requests / register
import requests

payload = {
    "type": 2,
    "fiscalId": "A1B2C3",
    "economicCode": "12345678901234",
    "name": "شرکت نمونه",
    "nationalCode": "10100000000",
    "mobile": "09120000000",
    "autoSend": True,
    "valueAddedNotCalled": True,
}

response = requests.post(
    "https://api-v2.asatsp.ir/api/register",
    headers={"Authorization": "Bearer <AccessToken with v2.register>"},
    json=payload,
    timeout=30,
)

print(response.json())
PHP cURL / register
$payload = [
    "type" => 2,
    "fiscalId" => "A1B2C3",
    "economicCode" => "12345678901234",
    "name" => "شرکت نمونه",
    "nationalCode" => "10100000000",
    "mobile" => "09120000000",
    "autoSend" => true,
    "valueAddedNotCalled" => true,
];

$ch = curl_init("https://api-v2.asatsp.ir/api/register");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer <AccessToken with v2.register>",
        "Content-Type: application/json; charset=utf-8",
    ],
    CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
]);

$result = curl_exec($ch);
curl_close($ch);

نمونه پاسخ سرویس ثبت مشتری

Register response
{
  "result": 1,
  "traceId": "6f9d0c76-1e78-4a61-9ec5-c63e38b49e3f",
  "message": "مشتری با موفقیت ایجاد شد.",
  "data": {
    "companyId": 1250,
    "fiscalId": "A1B2C3",
    "economicCode": "12345678901234",
    "valueAddedNotCalled": true,
    "customerApiKey": "9a1b2c3d4e5f",
    "serviceAuth": {
      "credential_type": "customer",
      "client_id": "v2-customer-1250",
      "client_secret": "shown-only-once",
      "allowed_scopes": "v2.invoice.cancel v2.invoice.read v2.invoice.send v2.lookup.read",
      "token_endpoint": "/api/auth/v2/token",
      "expires_in": 3600,
      "legacy_api_key_sunset_utc": "2026-10-08T00:00:00Z"
    },
    "created": true,
    "updated": false
  }
}
Tax JSON Registration

ثبت صورتحساب با JSON استاندارد سازمان امور مالیاتی

POST /api/invoice/send

این سرویس با Access Token دارای scope v2.invoice.send فراخوانی می‌شود و برای ثبت صورتحساب با ساختار رسمی سازمان امور مالیاتی به‌کار می‌رود. ورودی یک object شامل uuid و آرایه data با حداقل ۱ و حداکثر ۲۵۰ عضو است. فیلد uuid در ریشه بدنه، شناسه کل درخواست گروهی است و همه اعضای data[] همان درخواست، همین مقدار را به اشتراک می‌گذارند؛ برای هر درخواست جدید یک GUID تازه تولید کنید. هر عضو مستقل اعتبارسنجی و پردازش می‌شود؛ خطای یک صورتحساب مانع ثبت سایر صورتحساب‌های سالم نیست.

نتیجه هر عضو به همان ترتیب ورودی در data[] بازگردانده می‌شود. عضو ناموفق دارای status: 3 (hasError) و آرایه error است و مسیرهای ساخت‌یافته خطا نیز در errors[].path قرار می‌گیرند.
فیلد send اختیاری است. اگر این فیلد را ارسال نکنید، مقدار آن null باقی می‌ماند و تنظیم ارسال خودکار «API نسخه ۲» کسب‌وکار تصمیم نهایی را تعیین می‌کند؛ فقط true یا false صریح این تنظیم را برای همان عضو بازنویسی می‌کند.
صورتحساب موضوع ماده ۹: فیلد Header.insr «قاعده ارسال صورتحساب» است و برای ماده ۹ فقط مقدار عددی 1 دارد. فیلد Header.indati2m «تاریخ و زمان ثبت صورتحساب» و یک Unix timestamp عددی با دقت میلی‌ثانیه و حداکثر ۱۳ رقم است. مهلت فعلی سیستم ۱۵ روز کامل (۱۵ × ۲۴ ساعت) است و اعتبار نهایی هنگام ارسال بررسی می‌شود. اگر فاصله indatim تا زمان ارسال کمتر یا مساوی ۱۵ روز باشد، صورتحساب عادی است؛ حتی در صورت دریافت insr: 1، دو فیلد ماده ۹ در payload نهایی اعمال نمی‌شوند. برای صورتحساب خارج از این مهلت، insr: 1 و indati2m باید هم‌زمان ارسال شوند، تاریخ ثبت نباید از indatim کوچک‌تر یا از زمان ارسال بزرگ‌تر باشد و فاصله تاریخ ثبت تا ارسال نیز نباید بیش از ۱۵ روز باشد. این قابلیت برای صورتحساب‌های نوع اول و دوم، همه الگوهای مجاز آن‌ها و موضوع‌های اصلی، اصلاحی، برگشت از فروش و ابطالی قابل استفاده است.
indati2m از تاریخ صدور indatim مستقل است و تاریخ صدور، TaxId، سریال یا FiscalId را تغییر نمی‌دهد. در صورتحساب عادی، insr و indati2m اختیاری و خارج از الگو هستند؛ درخواست‌های قدیمی که این دو فیلد را ندارند بدون تغییر پشتیبانی می‌شوند.
نمونه کامل درخواست صورتحساب موضوع ماده ۹
{
  "uuid": "2e4f2ce6-cc0e-447b-a697-204dc96ea6e8",
  "data": [{
    "internalId": "ARTICLE9-1001",
    "send": true,
    "fiscalId": "PRD123",
    "sandBox": false,
    "data": {
      "Header": {
        "indatim": 1782940800000,
        "indati2m": 1784146800000,
        "insr": 1,
        "inty": 1,
        "inp": 1,
        "ins": 1,
        "inno": "A000000009",
        "tins": "12345678901234",
        "tob": 2,
        "tinb": "10987654321000",
        "bpc": "1234567890",
        "setm": 1,
        "tprdis": 10000000,
        "tdis": 0,
        "tadis": 10000000,
        "tvam": 1000000,
        "todam": 0,
        "tbill": 11000000,
        "cap": 11000000,
        "insp": 0,
        "tvop": 1000000
      },
      "Body": [{
        "sstid": "1234567890123",
        "sstt": "کالای نمونه",
        "mu": "162",
        "am": 2,
        "fee": 5000000,
        "prdis": 10000000,
        "dis": 0,
        "adis": 10000000,
        "vra": 10,
        "vam": 1000000,
        "odam": 0,
        "olam": 0,
        "tsstam": 11000000
      }],
      "Payments": []
    }
  }]
}

timestampها و شناسه‌های نمونه صرفاً برای نمایش قالب هستند؛ در درخواست واقعی، مقادیر متناظر با صورتحساب و زمان ثبت خود را ارسال کنید.

الگوی بورس اوراق بهادار مبتنی بر کالا (inp=11)

این الگو فقط در صورتحساب نوع اول (inty=1) و با دقیقاً یک ردیف در Body پذیرفته می‌شود. در Header، فیلدهای asn (شماره اعلامیه فروش بورس، فقط رقم و حداکثر ۳۰ رقم) و asd (تاریخ اعلامیه به روز Unix، حداکثر برابر روز تاریخ صدور) الزامی‌اند. در ردیف، mu، adis، vra، vam و tsstam الزامی‌اند؛ adis باید عیناً ارزش معامله در اعلامیه فروش بورس (ریال، عدد صحیح) باشد و vra و vam باید 0 باشند؛ بنابراین tsstam و tbill برابر adis است. در Header، tadis هم الزامی است و باید برابر adis تنها ردیف باشد. مبلغ واحد (fee) در این الگو خارج از الگوست؛ جزئیات در مبلغ واحد در الگوی ۱۱. cui (عیار) اختیاری است.
prdis، dis، Header.tprdis، Header.tdis، Header.todam و Payments در این الگو خارج از الگو هستند و نباید ارسال شوند؛ قاعده fee جداگانه در مبلغ واحد در الگوی ۱۱ آمده است. برای سازگاری، هر فیلد خارج از الگو با مقدار صفر یا رشته خالی (مانند dis، odam، olam، odr، cfee، odt، tdis، todam، tax17 یا crn)، آرایه خالی Payments، cui=0 و مقدار prdis/tprdis برابر با adis/tadis حذف می‌شود. مقدار غیرصفر یا غیرخالی فیلدهای خارج از الگو (تخفیف، سایر مالیات، فیلدهای ارزی، bsrn، crn، billid، tax17 و پرداخت‌ها) با خطای V79_OUT_OF_PATTERN رد می‌شوند. صورتحساب برگشت از فروش (ins=4) در این الگو مجاز نیست و در روش setm=3 جمع cap و insp باید برابر tbill باشد.
مبلغ واحد در الگوی ۱۱: طبق دستورالعمل RC_IITP.IS_V7.9، در الگوی بورس اوراق بهادار مبتنی بر کالا (inp=11) فیلد fee (مبلغ واحد) خارج از الگو است؛ نه الزامی است و نه اختیاری، و هرگز به سامانه مؤدیان ارسال نمی‌شود. مبلغ تعیین‌کننده و الزامی این الگو adis (مبلغ بعد از تخفیف) است که باید دقیقاً برابر ارزش معامله در اعلامیه فروش بورس، به ریال و عدد صحیح بزرگ‌تر از صفر باشد؛ لازم نیست بر am بخش‌پذیر باشد و گرد نمی‌شود.
  • fee را ارسال نکنید. اگر کلاینت قدیمی آن را بفرستد، فعلاً برای سازگاری، مقدار صحیح و غیرمنفی آن (حتی 0 یا مقداری ناهمخوان با adis و am) در مبلغ‌گذاری نادیده گرفته می‌شود و ذخیره نمی‌شود؛ ولی قالب آن همچنان کنترل می‌شود: مقدار اعشاری (مثلاً 333333.67 یعنی adis/am)، منفی یا غیرعددی خطای Body[i].fee می‌گیرد و همان عضو data[] ثبت نمی‌شود. با فعال شدن کنترل کامل Rule Matrix نسخه ۷.۹ برای الگوی ۱۱، هر مقدار fee (حتی 0) با خطای V79_OUT_OF_PATTERN رد خواهد شد.
  • adis را نمی‌توان حذف کرد: نبود یا null بودن adis خطای الزامی‌بودن می‌گیرد و سامانه آن را از fee × am یا prdis - dis محاسبه نمی‌کند. Header.tadis هم الزامی و برابر adis است.
  • مبلغ واحد ذخیره‌شده فقط نمایشی است: برای نمایش در پنل، چاپ و خروجی‌ها و سازگاری با ساختار ردیف، سامانه یک مبلغ واحد را از روی adis محاسبه و ذخیره می‌کند: کوتاه‌ترین عدد با حداکثر ۸ رقم اعشار که floor(مبلغ واحد × am) دقیقاً برابر adis شود؛ برای adis=1000001 و am=3 این مقدار 333333.7 است. این مقدار به سامانه مؤدیان ارسال نمی‌شود و در پاسخ سرویس‌های ثبت و پیگیری API نسخه ۲ هم برنمی‌گردد.
  • تخفیف: prdis، dis، Header.tprdis و Header.tdis خارج از الگو هستند؛ مقدار صفر (و prdis/tprdis برابر adis/tadis) حذف و هر مقدار دیگر با V79_OUT_OF_PATTERN رد می‌شود. جزئیات در جدول اعتبارسنجی JSON.

خلاصه رفتار مبلغ واحد و مبلغ بعد از تخفیف الگوی ۱۱ در مسیرهای API:

مسیرمبلغ واحد (fee / unitPrice)مبلغ بعد از تخفیف (adis / amountAfterDiscount)ارسال به سامانه مؤدیان
JSON استاندارد: /api/invoice/sendfee خارج از الگوست؛ ارسال نکنید. اگر ارسال شود باید عدد صحیح غیرمنفی باشد (مقدار اعشاری یا منفی خطا می‌گیرد)؛ سپس نادیده گرفته می‌شود و ذخیره نمی‌شود.adis الزامی: عدد صحیح بزرگ‌تر از صفر برابر ارزش معامله در اعلامیه فروش بورس؛ از fee یا prdis محاسبه نمی‌شود. Header.tadis برابر آن است.adis و am عیناً؛ fee، prdis و dis ارسال نمی‌شوند.
مدل ساده: /api/invoice/commoditySecurities و /api/invoice/simple/type/1/template/11unitPrice فقط در نبود amountAfterDiscount لازم است و به‌کار می‌رود؛ floor(qty × unitPrice) باید بزرگ‌تر از صفر باشد (اعشار مجاز است). در کنار amountAfterDiscount نادیده گرفته می‌شود ولی نباید منفی باشد.amountAfterDiscount (روش توصیه‌شده): عدد صحیح بزرگ‌تر از صفر برابر ارزش معامله در اعلامیه فروش بورس؛ بر unitPrice مقدم است و عیناً adis می‌شود.همان adis به‌دست‌آمده و am؛ unitPrice ارسال نمی‌شود.
پاسخ پیگیری: inquiryInternalId، inquiryNo، inquiryTaxId و نسخه‌های آرایه‌ایبرنمی‌گردد؛ پاسخ پیگیری فیلد ردیف ندارد و مبلغ واحد نمایشی (مثلاً 333333.7) فقط در پنل، چاپ و خروجی دیده می‌شود.فقط در status: 2 و در json.Body[0].adis.json همان payload ارسال‌شده است: در json.Body[0] adis و am مقدار دارند ولی fee، prdis و dis مقداری ندارند (برابر null هستند یا اصلاً نمی‌آیند).
اصلاحی الگوی ۱۱ و اعلامیه فروش بورس: طبق دستورالعمل RC_IITP.IS_V7.9، صدور صورتحساب اصلاحی (ins=2) برای زنجیره الگوی ۱۱ فقط تا زمانی مجاز است که اعلامیه فروش بورس تأیید نشده باشد؛ پس از تأیید فقط ابطال (ins=3) مجاز است. سامانه راهی برای دریافت خودکار این تأیید ندارد؛ وضعیت آن را با /api/invoice/exchangeAnnouncement یا در پنل ثبت کنید. تا وقتی وضعیتی ثبت نشده، اصلاحی مانند گذشته پذیرفته می‌شود. پس از ثبت «تأیید شده»، اصلاحی همان زنجیره (حتی اگر inp اصلاحی ۱۱ نباشد) با کد EXCHANGE_ANNOUNCEMENT_CONFIRMED، و اگر وضعیت موقتاً قابل خواندن نباشد با کد EXCHANGE_ANNOUNCEMENT_STATUS_UNAVAILABLE، در مسیر $.data[i] رد می‌شود. ابطال، برگشت از فروش و سایر اعضای درخواست گروهی تحت تأثیر قرار نمی‌گیرند.
نمونه کامل درخواست الگوی بورس اوراق بهادار مبتنی بر کالا
{
  "uuid": "6b1f0f3e-3c8a-4c61-9f7e-3f2d7a5b9c11",
  "data": [{
    "internalId": "IME-1001",
    "send": true,
    "fiscalId": "PRD123",
    "sandBox": false,
    "data": {
      "Header": {
        "indatim": 1788271140000,
        "inty": 1,
        "inp": 11,
        "ins": 1,
        "inno": "A000000011",
        "tins": "12345678901234",
        "tob": 2,
        "tinb": "10987654321000",
        "setm": 3,
        "tadis": 1000001,
        "tvam": 0,
        "tbill": 1000001,
        "cap": 400000,
        "insp": 600001,
        "asn": "1405012345",
        "asd": 20697
      },
      "Body": [{
        "sstid": "1234567890123",
        "sstt": "کالای معامله‌شده در بورس کالا",
        "mu": "162",
        "am": 3,
        "adis": 1000001,
        "vra": 0,
        "vam": 0,
        "tsstam": 1000001
      }]
    }
  }]
}

در این نمونه adis=1000001 بر am=3 بخش‌پذیر نیست و همان مبلغ بدون گرد شدن به‌عنوان مبلغ بعد از تخفیف ذخیره و ارسال می‌شود. fee ارسال نشده است؛ مبلغ واحد نمایشی که سامانه ذخیره می‌کند 333333.7 است (floor(333333.7 × 3) = 1000001) و به سامانه مؤدیان ارسال نمی‌شود. asd=20697 همان روز 2026/09/01 است که با روز تاریخ صدور برابر است.

برای اصلاحی الگوی ۱۱ (فقط پیش از تأیید اعلامیه فروش بورس؛ قاعده اصلاحی) همین ساختار را با ins=2، شماره مالیاتی صورتحساب مرجع در irtaxid، asn و asd اعلامیه و adis جدید بفرستید؛ در اصلاحی هم fee، prdis و dis خارج از الگو هستند و نباید ارسال شوند.

نمونه اصلاحی الگوی بورس اوراق بهادار مبتنی بر کالا
{
  "uuid": "9a3e7c21-54b0-4f8e-a1d6-2c7b9e0f4a13",
  "data": [{
    "internalId": "IME-1001-EDIT",
    "send": true,
    "fiscalId": "PRD123",
    "sandBox": false,
    "data": {
      "Header": {
        "indatim": 1788274740000,
        "inty": 1,
        "inp": 11,
        "ins": 2,
        "irtaxid": "A1B2C304E7900000F42410",
        "inno": "A000000012",
        "tins": "12345678901234",
        "tob": 2,
        "tinb": "10987654321000",
        "setm": 3,
        "tadis": 1000500,
        "tvam": 0,
        "tbill": 1000500,
        "cap": 400000,
        "insp": 600500,
        "asn": "1405012345",
        "asd": 20697
      },
      "Body": [{
        "sstid": "1234567890123",
        "sstt": "کالای معامله‌شده در بورس کالا",
        "mu": "162",
        "am": 3,
        "adis": 1000500,
        "vra": 0,
        "vam": 0,
        "tsstam": 1000500
      }]
    }
  }]
}

ساختار کلی درخواست

tax JSON registration request shape
{
  "uuid": "GUID یکتای درخواست",
  "data": [
    {
      "internalId": "کد داخلی یکتای صورتحساب",
      "send": true,
      "fiscalId": "SND123",
      "sandBox": true,
      "description": "توضیحات اختیاری",
      "data": {
        "Header": {},
        "Body": [],
        "Payments": []
      }
    }
  ]
}
درخواست گروهی

آرایه data باید بین ۱ تا ۲۵۰ عضو داشته باشد و هر عضو مستقل اعتبارسنجی و ذخیره می‌شود.

شناسه درخواست و جلوگیری از تکرار

uuid به کل درخواست تعلق دارد، نه به یک شرکت یا یک صورتحساب. کنترل تکراری‌بودن آن فقط در محدوده شرکت احراز هویت‌شده انجام می‌شود؛ بنابراین همان شرکت نباید uuid یک درخواست قبلی را دوباره استفاده کند. internalId نیز باید برای هر صورتحساب آن شرکت یکتا بماند.

Ownership

Header.tins باید با کد اقتصادی شرکت احراز هویت‌شده برابر باشد.

Fiscal Memory & Environment

چند شناسه حافظه مالیاتی و محیط Sandbox

هر کسب‌وکار می‌تواند چند شناسه حافظه مالیاتی داشته باشد. هر شناسه دقیقاً متعلق به یکی از محیط‌های Production یا Sandbox است و هر صورتحساب هنگام ثبت به همان شناسه متصل می‌شود. Serial و TaxId بر اساس همان شناسه تولید می‌شوند؛ تغییر Default شرکت، شناسه یا اطلاعات صورتحساب‌های قبلی را تغییر نمی‌دهد.

فیلدهای درخواست

فیلدنوعالزامقاعده
fiscalIdstring(6)اختیاریشناسه یکتای شش‌کاراکتری حافظه مالیاتی؛ باید متعلق به همان کسب‌وکار، فعال و با محیط درخواست سازگار باشد. در صورت ارسال، همان شناسه روی Invoice تثبیت می‌شود.
sandBoxbooleanاختیاریtrue برای Sandbox و false برای Production. مقدار پیش‌فرض false است؛ در صورت ارسال‌نشدن، درخواست عملیاتی محسوب می‌شود.
شناسه Sandbox باید مستقلاً از محیط https://sandboxrc.tax.gov.ir اخذ و در همان محیط استعلام شود. حتی اگر مقدار شش‌کاراکتری آن با شناسه عملیاتی یکسان باشد، ثبت Production و Sandbox دو رکورد مستقل هستند و انجام مراحل اخذ/فعال‌سازی در یک محیط، جایگزین محیط دیگر نیست.

انتخاب خودکار در نبود fiscalId

۱. محیط درخواستProduction یا Sandbox مشخص می‌شود؛ fallback بین دو محیط مجاز نیست.
۲. Default فعال همان محیطDefault محیط دیگر در انتخاب شرکت نمی‌کند.
۳. تنها شناسه فعال همان محیطاگر فقط یک شناسه قابل استفاده باشد، خودکار انتخاب می‌شود.
۴. انتخاب صریحدر صورت وجود چند شناسه فعال بدون Default، ارسال fiscalId شش‌کاراکتری الزامی است.

نمونه درخواست‌ها

Production با شناسه صریح
{
  "uuid": "0df9e8c6-07ba-4dc6-93f2-c20f5f4aef08",
  "data": [{
    "internalId": "PROD-1001",
    "send": true,
    "fiscalId": "PRD123",
    "sandBox": false,
    "data": { "Header": {}, "Body": [], "Payments": [] }
  }]
}
Sandbox با شناسه صریح
{
  "uuid": "8fd4439c-7964-4ac4-aee0-f6fd2dfadff8",
  "data": [{
    "internalId": "SANDBOX-1001",
    "send": false,
    "fiscalId": "SND123",
    "sandBox": true,
    "data": { "Header": {}, "Body": [], "Payments": [] }
  }]
}
استفاده از Default محیط و تنظیم ارسال API نسخه ۲
{
  "uuid": "5ec152a0-a8e8-4ee1-9bbf-73f7954b38ee",
  "data": [{
    "internalId": "SANDBOX-1002",
    "sandBox": true,
    "data": { "Header": {}, "Body": [], "Payments": [] }
  }]
}
کلاینت قدیمی بدون فیلدهای جدید
{
  "uuid": "17c0f06d-5bf1-40dc-b3cc-61b76677d26e",
  "data": [{
    "internalId": "LEGACY-1001",
    "send": true,
    "data": { "Header": {}, "Body": [], "Payments": [] }
  }]
}

فیلدهای پاسخ

Fiscal memory binding
{
  "fiscalMemoryId": 31,
  "fiscalId": "ABC123",
  "fiscalMemoryTitle": "شناسه تست API",
  "sandBox": true,
  "environment": "Sandbox",
  "environmentTitle": "سندباکس",
  "isDefaultFiscalMemory": true
}

خطاهای انتخاب شناسه

این پیام‌ها عین پیام‌های Validator مرکزی هستند و در خطای عضو مربوطه بازگردانده می‌شوند. برای جلوگیری از افشای مالکیت، «یافت نشدن» و «متعلق‌بودن به شرکت دیگر» یک پیام امن مشترک دارند.

وضعیتپیام واقعی APIاقدام اصلاحی
شناسه یافت نشد یا متعلق به شرکت دیگر استFiscalId حافظه مالیاتی انتخاب‌شده متعلق به این کسب‌وکار نیست.یک fiscalId شش‌کاراکتری متعلق به شرکت احراز هویت‌شده ارسال کنید.
شناسه غیرفعال استشناسه حافظه مالیاتی انتخاب‌شده غیرفعال است و برای صورتحساب جدید قابل استفاده نیست.شناسه‌ای با IsEnabled = true انتخاب کنید.
محیط ناسازگار استFiscalId حافظه مالیاتی انتخاب‌شده در محیط عملیاتی/سندباکس این کسب‌وکار ثبت نشده است.مقدار sandBox یا شناسه را اصلاح کنید؛ fallback بین محیط‌ها انجام نمی‌شود.
چند شناسه بدون Defaultدر محیط عملیاتی/سندباکس چند شناسه حافظه مالیاتی فعال وجود دارد و هیچ شناسه پیش‌فرضی تعیین نشده است؛ شناسه حافظه مالیاتی باید صریح انتخاب شود.Default همان محیط را تعیین یا fiscalId را صریح ارسال کنید.

نمونه‌های خطا

در ثبت گروهی، خطای انتخاب شناسه در عضو متناظر data ثبت می‌شود. قطعه‌های زیر مقدار ورودی و پیام واقعی همان عضو را نشان می‌دهند.

شناسه متعلق به شرکت دیگر یا ناموجود
{
  "request": { "fiscalId": "ZZ9999", "sandBox": false },
  "error": "FiscalId حافظه مالیاتی انتخاب‌شده متعلق به این کسب‌وکار نیست."
}
شناسه غیرفعال
{
  "request": { "fiscalId": "OFF123", "sandBox": false },
  "error": "شناسه حافظه مالیاتی انتخاب‌شده غیرفعال است و برای صورتحساب جدید قابل استفاده نیست."
}
ناسازگاری محیط
{
  "request": { "fiscalId": "SND123", "sandBox": false },
  "error": "FiscalId حافظه مالیاتی انتخاب‌شده در محیط عملیاتی این کسب‌وکار ثبت نشده است."
}
چند شناسه فعال بدون Default
{
  "request": { "sandBox": true },
  "error": "در محیط سندباکس چند شناسه حافظه مالیاتی فعال وجود دارد و هیچ شناسه پیش‌فرضی تعیین نشده است؛ شناسه حافظه مالیاتی باید صریح انتخاب شود."
}
شناسه Sandbox به دلیل isActive = false غیرفعال نیست. صورتحساب Sandbox فقط با شناسه Sandbox ثبت و به مقصد Sandbox ارسال می‌شود؛ شناسه Production در درخواست Sandbox و بالعکس پذیرفته نمی‌شود.
Batch Registration Contract

رفتار ثبت گروهی، خطای جزئی و محدودیت درخواست

قواعد این بخش برای /api/invoice/send و همه Endpointهای مدل ساده آرایه‌ای، از جمله /api/invoice/salesWithBuyerData، یکسان است.

موضوعرفتار APIاقدام پیشنهادی مصرف‌کننده
خطای اعتبارسنجی یک یا چند عضوهر عضو مستقل اعتبارسنجی می‌شود. صورتحساب‌های سالم ثبت می‌شوند و فقط اعضای نامعتبر با status: 3 و جزئیات error[] برمی‌گردند.فقط اعضای ناموفق را پس از اصلاح، با uuid جدید دوباره ارسال کنید.
ترتیب پاسخآرایه پاسخ data دقیقاً با ترتیب آرایه ورودی متناظر است؛ هر ورودی یک نتیجه موفق یا ناموفق دارد.برای تطبیق قطعی علاوه بر index، از internalId استفاده کنید.
خطای غیرمنتظره هنگام ذخیرهخطای اجرایی یک عضو ثبت اعضای بعدی را متوقف نمی‌کند و همان عضو با خطا برمی‌گردد. چون ذخیره هر صورتحساب مستقل است، نتیجه تک‌تک اعضا را بررسی کنید.در timeout یا قطع ارتباط، پیش از retry وضعیت internalIdها را استعلام کنید.
خطای گذرای پایگاه دادهاگر ثبت یک عضو به‌دلیل ازدحام یا بن‌بست پایگاه داده کامل نشود، همان عضو با status: 3 و خطایی با code: "INVOICE_DATABASE_TEMPORARILY_UNAVAILABLE" در errors[].path = $.data[i] برمی‌گردد. این خطا رد صورتحساب نیست و اعضای دیگر تحت تأثیر قرار نمی‌گیرند.وضعیت همان internalId را با inquiryInternalId استعلام کنید و اگر ثبت نشده بود، فقط همان صورتحساب را در یک درخواست جدید با uuid جدید دوباره ارسال کنید.
تعداد اعضای dataحداقل ۱ و حداکثر ۲۵۰ صورتحساب در هر درخواست مجاز است. درخواست دارای ۲۵۱ عضو یا بیشتر، پیش از پردازش کل آرایه رد می‌شود.برای بیش از ۲۵۰ صورتحساب چند درخواست با uuidهای مستقل بسازید.
حجم و زمان درخواستعلاوه بر سقف ۲۵۰ عضو، محدودیت حجم JSON، وب‌سرور، پراکسی و timeout محیط استقرار نیز اعمال می‌شود.حجم serialized JSON و timeout کلاینت را کنترل کنید.
مقدار resultresult: 1 یعنی حداقل یک صورتحساب ثبت شده است؛ حتی اگر تعدادی عضو خطا داشته باشند. result: 0 یعنی هیچ صورتحسابی ثبت نشده یا ساختار کلی درخواست رد شده است.برای تشخیص موفقیت کامل، علاوه بر result، تمام اعضای data[] و errors[] را بررسی کنید.
مثال: اگر آرایه شامل ۲۵۰ صورتحساب باشد و ۳ مورد خطا داشته باشند، ۲۴۷ مورد سالم ثبت می‌شوند و همان ۳ عضو با وضعیت خطا در پاسخ برمی‌گردند. ارسال ۱۰۰۰ صورتحساب در یک درخواست مجاز نیست و باید حداقل به ۴ درخواست تقسیم شود.
برای سازگاری با کلاینت‌های قدیمی، اگر Rule Matrix نسخه ۷.۹ فیلدهای Header.tonw، Header.sg یا Body.nw را در صورتحساب نوع اول یا دوم خارج از الگو بداند، API آن‌ها را پیش از اعتبارسنجی نادیده می‌گیرد. در صورتحساب نوع دوم همین رفتار برای Header.setm، Header.cap، Header.insp، Header.tvop، Body.cop و Body.vop نیز اعمال می‌شود. این سازگاری فقط به همین فیلدهای امن محدود است و سایر فیلدهای خارج از الگو همچنان با خطای ساخت‌یافته رد می‌شوند. فیلد Header.bbc نادیده گرفته نمی‌شود و در صورت ارسال باید کد واقعی شعبه خریدار با دقیقاً ۴ رقم باشد.

قاعده تکرار امن درخواست

  1. uuid را برای هر درخواست جدید یکتا تولید کنید و internalId هر صورتحساب را ثابت و یکتا نگه دارید.
  2. اگر پاسخ شامل نتیجه جزئی بود، اعضای دارای status: 3 را اصلاح کنید؛ اعضای موفق را دوباره نفرستید.
  3. اگر result: 0 دریافت شد، هیچ عضوی ثبت نشده است؛ خطاهای ریشه یا تمام اعضای ناموفق را بررسی کنید.
  4. اگر timeout، قطع ارتباط یا خطای اجرایی رخ داد، ابتدا با Endpointهای استعلام، وضعیت تک‌تک internalIdها را بررسی کنید.
  5. فقط صورتحساب‌های ثبت‌نشده را با uuid جدید دوباره ارسال کنید.
Send Examples

نمونه‌های چندزبانه ثبت صورتحساب

نمونه زیر ساختار یک درخواست گروهی دو عضوی را نشان می‌دهد. برای کوتاه ماندن نمونه، عضو دوم می‌تواند با همان ساختار عضو اول و با internalId و inno متفاوت ساخته شود.

Group JSON / tax invoice registration
{
  "uuid": "9f806c1a-2c7e-4d1d-a3fa-2c65fd8c3210",
  "data": [
    {
      "internalId": "1000001",
      "send": true,
      "fiscalId": "PRD123",
      "sandBox": false,
      "description": "فروش نقدی نمونه",
      "data": {
        "Header": {
          "indatim": 1782940800000,
          "inty": 1,
          "inp": 1,
          "ins": 1,
          "inno": "A000000001",
          "tins": "12345678901234",
          "tob": 2,
          "tinb": "10987654321000",
          "bpc": "1234567890",
          "setm": 1,
          "tprdis": 10000000,
          "tdis": 0,
          "tadis": 10000000,
          "tvam": 1000000,
          "todam": 0,
          "tbill": 11000000,
          "cap": 11000000,
          "insp": 0,
          "tvop": 1000000
        },
        "Body": [
          {
            "sstid": "1234567890123",
            "sstt": "کالای نمونه",
            "mu": "162",
            "am": 2,
            "fee": 5000000,
            "prdis": 10000000,
            "dis": 0,
            "adis": 10000000,
            "vra": 10,
            "vam": 1000000,
            "odam": 0,
            "olam": 0,
            "tsstam": 11000000
          }
        ],
        "Payments": [
          {
            "pdt": 1782940800000,
            "pmt": 2,
            "pv": 11000000
          }
        ]
      }
    },
    {
      "internalId": "1000002",
      "send": false,
      "fiscalId": "SND123",
      "sandBox": true,
      "description": "ثبت بدون صف ارسال خودکار",
      "data": {
        "Header": {
          "indatim": 1782940800000,
          "inty": 1,
          "inp": 1,
          "ins": 1,
          "inno": "A000000002",
          "tins": "12345678901234",
          "tob": 2,
          "tinb": "10987654321000",
          "bpc": "1234567890",
          "setm": 1,
          "tprdis": 5000000,
          "tdis": 0,
          "tadis": 5000000,
          "tvam": 500000,
          "todam": 0,
          "tbill": 5500000,
          "cap": 5500000,
          "insp": 0,
          "tvop": 500000
        },
        "Body": [
          {
            "sstid": "1234567890123",
            "sstt": "خدمت نمونه",
            "mu": "162",
            "am": 1,
            "fee": 5000000,
            "prdis": 5000000,
            "dis": 0,
            "adis": 5000000,
            "vra": 10,
            "vam": 500000,
            "odam": 0,
            "olam": 0,
            "tsstam": 5500000
          }
        ],
        "Payments": [
          {
            "pdt": 1782940800000,
            "pmt": 2,
            "pv": 5500000
          }
        ]
      }
    }
  ]
}
cURL / register tax invoice
curl -X POST "https://api-v2.asatsp.ir/api/invoice/send" \
  -H "Authorization: Bearer <AccessToken with v2.invoice.send>" \
  -H "Content-Type: application/json; charset=utf-8" \
  --data-binary "@invoice-batch.json"
JavaScript fetch / register tax invoice
const payload = {
  uuid: crypto.randomUUID(),
  data: [
    {
      internalId: "1000001",
      send: true,
      fiscalId: "SND123",
      sandBox: true,
      description: "فروش نقدی نمونه",
      data: invoiceDto
    }
  ]
};

const response = await fetch("https://api-v2.asatsp.ir/api/invoice/send", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <AccessToken with v2.invoice.send>",
    "Content-Type": "application/json; charset=utf-8"
  },
  body: JSON.stringify(payload)
});

const result = await response.json();
if (result.result !== 1) {
  console.table(result.errors || []);
}
C# HttpClient / register tax invoice
using System.Net.Http;
using System.Text;

var json = File.ReadAllText("invoice-batch.json");

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer <AccessToken with v2.invoice.send>");

var response = await client.PostAsync(
    "https://api-v2.asatsp.ir/api/invoice/send",
    new StringContent(json, Encoding.UTF8, "application/json"));

var body = await response.Content.ReadAsStringAsync();
Python requests / register tax invoice
import json
import requests

with open("invoice-batch.json", "r", encoding="utf-8") as file:
    payload = json.load(file)

response = requests.post(
    "https://api-v2.asatsp.ir/api/invoice/send",
    headers={"Authorization": "Bearer <AccessToken with v2.invoice.send>"},
    json=payload,
    timeout=60,
)

result = response.json()
if result.get("result") != 1:
    for error in result.get("errors", []):
        print(error["path"], error["message"])
PHP cURL / register tax invoice
$payload = file_get_contents("invoice-batch.json");

$ch = curl_init("https://api-v2.asatsp.ir/api/invoice/send");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer <AccessToken with v2.invoice.send>",
        "Content-Type: application/json; charset=utf-8",
    ],
    CURLOPT_POSTFIELDS => $payload,
    CURLOPT_TIMEOUT => 60,
]);

$result = curl_exec($ch);
curl_close($ch);
V1 Compatible Input

ثبت صورتحساب با مدل ساده و آرایه‌ای

برای سامانه‌هایی که نیاز ندارند JSON رسمی سامانه مؤدیان را به‌صورت مستقیم تولید کنند، Endpointهای زیر مدل ورودی ساده و سازگار با نسخه ۱ را دریافت می‌کنند. بدنه همه این مسیرها یک object شامل uuid در ریشه و آرایه data با حداکثر ۲۵۰ عضو است. در این مدل نیز uuid شناسه کل درخواست است، بین همه اعضای data[] همان درخواست مشترک است و باید برای هر درخواست جدید مقدار تازه‌ای داشته باشد. هر عضو باید internalId یکتا داشته باشد و مستقل پردازش می‌شود؛ خطای یک عضو مانع ثبت اعضای سالم نیست.

در تمام Endpointهای ساده، هر عضو data[] همانند مسیر /api/invoice/send فیلدهای send، fiscalId و sandBox را می‌پذیرد. اگر send حذف شود، مقدار آن null می‌ماند و تنظیم ارسال خودکار «API نسخه ۲» کسب‌وکار اعمال می‌شود. برای ثبت سندباکس، sandBox: true و شناسه شش‌کاراکتری همان محیط را کنار send ارسال کنید؛ این انتخاب برای هر عضو جداگانه اعمال می‌شود. فیلدهای اختیاری isArticle9 و invoiceRegistrationDateTime نیز ماده ۹ و تاریخ ثبت Unix میلی‌ثانیه‌ای آن را مشخص می‌کنند.

ترکیب‌های مجاز نوع و الگو از طریق مسیر عمومی /api/invoice/simple/type/{invoiceType}/template/{template} نیز قابل ثبت هستند. طبق دستورالعمل RC_IITP.IS V7.9، نوع اول الگوهای 1، 2، 3، 4، 5، 6، 7، 8، 9، 11، 13 و 14 را می‌پذیرد؛ نوع دوم فقط برای الگوهای 1، 3، 9 و 13 مجاز است.

الگونوع اولنوع دوم
inp=1 فروش کالا و خدمات/api/invoice/salesWithBuyerData/api/invoice/salesEndUser
inp=2 فروش ارز/api/invoice/exchangeدر نوع دوم مجاز نیست
inp=3 طلا، جواهر و پلاتین/api/invoice/jewelry/api/invoice/jewelryEndUser
inp=4 قرارداد پیمانکاری/api/invoice/contractingدر نوع دوم مجاز نیست
inp=5 قبوض خدماتی/api/invoice/utilityBillدر نوع دوم مجاز نیست
inp=6 بلیت هواپیما/api/invoice/flightTicketدر نوع دوم مجاز نیست
inp=7 صادرات/api/invoice/exportدر نوع دوم مجاز نیست
inp=8 بارنامه/api/invoice/billOfLadingدر نوع دوم مجاز نیست
inp=9 فروش فرآورده‌های نفتی پالایش و پخش/api/invoice/oilProducts/api/invoice/oilProductsEndUser
inp=11 بورس اوراق بهادار مبتنی بر کالا/api/invoice/commoditySecuritiesدر نوع دوم مجاز نیست
inp=13 بیمه/api/invoice/insurance/api/invoice/insuranceEndUser
inp=14 فروش زنجیره/api/invoice/chainSaleدر نوع دوم مجاز نیست

در مسیرهای نوع اول، ارسال اطلاعات خریدار در buyer الزامی است؛ تنها الگوی صادرات inp=7 از این الزام مستثنی است. در مسیرهای نوع دوم، اطلاعات خریدار اختیاری است.

قرارداد کامل اطلاعات خریدار (buyer)

شیء buyer در هر عضو data قرار می‌گیرد. نام فیلدها را مطابق جدول زیر ارسال کنید. همه شناسه‌ها و شماره‌های تماس باید از نوع JSON string باشند تا صفر ابتدای مقدار حذف نشود.

فیلدنوعالزامقالب و توضیح
typenumberخیر؛ پیش‌فرض 11 حقیقی، 2 حقوقی، 3 مشارکت مدنی، 4 اتباع غیر ایرانی. برای جلوگیری از برداشت نادرست، ارسال صریح آن توصیه می‌شود.
nationalCodestringشرطیفقط رقم؛ در قرارداد ورودی 5 تا 14 رقم. برای شخص حقیقی ایرانی کد ملی معتبر 10 رقمی و برای شخص حقوقی شناسه ملی 11 رقمی ارسال شود. در صورت نداشتن economicCode الزامی است.
economicCodestringبرای نوع 2 الزامیفقط رقم، 11 تا 14 رقم. حداقل یکی از nationalCode یا economicCode باید موجود باشد.
zipCodestringخیرکد پستی 10 رقمی؛ بدون خط تیره یا فاصله.
namestringخیرنام شخص یا عنوان خریدار. حداکثر ظرفیت ذخیره‌سازی 4000 کاراکتر است.
mobilestringخیرشماره همراه ایران با قالب پیشنهادی 09xxxxxxxxx و 11 رقم. مقدار معتبر به همین قالب نرمال می‌شود.
addressstringخیرنشانی پستی به‌صورت متن Unicode، حداکثر 4000 کاراکتر.
phonestringخیرشماره تلفن ثابت همراه پیش‌شماره؛ الگوی عددی جداگانه‌ای در endpoint اعمال نمی‌شود و حداکثر ظرفیت ذخیره‌سازی 1024 کاراکتر است.
branchCodenumberخیرکد شعبه خریدار به‌صورت عدد صحیح.
passportNumberstringشرطیشماره گذرنامه خریدار غیرایرانی که کد فراگیر ندارد؛ حداکثر ۹ حرف و رقم لاتین. فقط با type=4 در الگوی فروش ارز (template=2) پذیرفته می‌شود و به‌جای nationalCode به‌عنوان header.bpn ارسال می‌شود.
فیلدهای mobile، address و phone اطلاعات تکمیلی هستند و جایگزین شناسه‌های هویتی خریدار نمی‌شوند. برای خریدار حقوقی، economicCode حتی در صورت ارسال nationalCode همچنان الزامی است.

نمونه کامل buyer

buyer object
"buyer": {
  "type": 2,
  "nationalCode": "10100000000",
  "economicCode": "10987654321000",
  "zipCode": "1234567890",
  "name": "شرکت خریدار نمونه",
  "mobile": "09120000000",
  "address": "تهران، خیابان نمونه، پلاک ۱",
  "phone": "02188770000",
  "branchCode": 1
}

کد داخلی کالا/خدمت در مدل ساده

برای هر قلم در آرایه items می‌توانید کد داخلی همان کالا یا خدمت را مطابق قرارداد زیر ارسال کنید.

فیلدنوعالزامقالب و توضیح
items[].internalIdstringخیرکد یا شناسه کالا/خدمت در سامانه مبدأ شما، حداکثر ۲۵۴ کاراکتر. API کالا/خدمت را با این کد شناسایی یا ایجاد می‌کند و مقدار را به‌عنوان کد داخلی آن ذخیره می‌کند. برای حفظ صفرهای ابتدایی باید به‌صورت JSON string ارسال شود.
items[].internalId با data[].internalId (کد داخلی صورتحساب) و items[].stuffId تفاوت دارد. فیلد stuffId شناسه رسمی ۱۳رقمی کالا/خدمت است و به Body[].sstid نگاشت می‌شود؛ کد داخلی کالا/خدمت جایگزین آن نیست و در payload ارسالی به سامانه مودیان قرار نمی‌گیرد. برابر بودن اختیاری مقدار internalId و stuffId مجاز است. اگر internalId ارسال نشود، رفتار موجود سامانه برای تولید کد داخلی حفظ می‌شود.

مقادیر شمارشی مدل ساده

فیلدمقادیر مجازنکته
invoiceSubject1 اصلی، 2 اصلاحی، 3 ابطالی، 4 برگشت از فروشبرای موضوع‌های غیر اصلی، ارسال sourceInternalId یا sourceTaxId الزامی است.
isArticle9true یا false (اختیاری)در حالت true، فیلد invoiceRegistrationDateTime اجباری است؛ حذف فیلد یا false رفتار قبلی را حفظ می‌کند.
invoiceRegistrationDateTimeUnix Time میلی‌ثانیه‌ای، حداکثر ۱۳ رقمتاریخ و زمان ثبت صورتحساب موضوع ماده ۹؛ با تاریخ صدور dateTime متفاوت است.
settlement1 نقدی، 2 نسیه، 3 نقدی/نسیهدر حالت 3، مقدار payment سهم نقدی تسویه است.
buyer.type1 حقیقی، 2 حقوقی، 3 مشارکت مدنی، 4 اتباع غیر ایرانیبرای خریدار حقوقی، economicCode الزامی است.
payments[].type1 چک، 2 تهاتر، 3 وجه نقد، 4 کارت‌خوان (POS)، 5 درگاه پرداخت اینترنتی، 6 کارت‌به‌کارت، 7 انتقال به حساب، 8 سایراین مقدار به Payments[].pmt نگاشت می‌شود. برای پرداخت کارت‌خوانی نوع 4، شماره پذیرنده، پایانه و کارت الزامی است.

فیلدهای اختصاصی هر الگو

الگوفیلدهای سادهتوضیح
inp=1 فروشdeclarationNo, officeCodeاختیاری و فقط برای صورتحساب نوع اول: شماره پروانه گمرکی (header.scln، حداکثر 14 رقم) و کد گمرک محل اظهار (header.scc، دقیقاً 5 رقم).
inp=2 ارزitems[].currencyCode, amountCurrency, currencyRate, currencyPurchaseRate, vatBaseAmount, declarationNo, officeCode, buyer.passportNumberنرخ خرید ارز باید کوچک‌تر از نرخ فروش ارز باشد. declarationNo و officeCode مثل الگوی ۱. خریدار غیرایرانی فاقد کد فراگیر با buyer.type=4 و buyer.passportNumber ثبت می‌شود.
inp=3 طلا، جواهر و پلاتینitems[].wage, profit, commission, caratاجرت ساخت، سود فروشنده و حق‌العمل الزامی و غیرمنفی هستند.
inp=4 پیمانکاریcontractCodeشناسه یکتای ثبت قرارداد پیمانکاری، 12 رقم.
inp=5 قبوض خدماتیbillNoشناسه قبض یا شماره اشتراک بهره‌بردار، حداکثر 19 رقم.
inp=6 بلیت هواپیماflightInfoflightType: 1 داخلی، 2 خارجی. buyerMethod: 1 شماره ملی، 2 کد فراگیر اتباع، 3 گذرنامه.
inp=7 صادراتcottageNo, cottageDate, officeCode, items[].netWeight, currencyValue, currencyCode, currencyRateاگر تاریخ کوتاژ ارسال شود، شماره کوتاژ هم الزامی است. officeCode (کد گمرک محل اظهار، header.scc) اختیاری و دقیقاً 5 رقم است؛ شماره پروانه گمرکی در این الگو خارج از الگوست.
inp=8 بارنامهshipmentInfoشماره بارنامه، مبدأ/مقصد، فرستنده/گیرنده، نوع حمل، ناوگان و وزن کل کنترل می‌شوند.
inp=9 فرآورده‌های نفتی پالایش و پخشفیلدهای عمومی کالا/خدمت و مالیات، items[].vatBaseAmountvatBaseAmount مبلغ پایه مالیات بر ارزش افزوده (body[].vba) است؛ اگر بزرگ‌تر از صفر باشد، مالیات ردیف از همین مبلغ محاسبه می‌شود و در غیر این صورت مبلغ بعد از تخفیف مبناست.
inp=11 بورس اوراق بهادار مبتنی بر کالاsaleAnnNo, saleAnnDate, items[].amountAfterDiscount, items[].caratشماره اعلامیه فروش باید عددی باشد و تاریخ اعلامیه الزامی است. تاریخ تقویمی بدون جابه‌جایی روز به asd تبدیل می‌شود؛ برای Unix time، روز تقویمی تهران مبناست. برای نمونه، 20260901 معادل 1405/06/10 و asd=20697 است. تاریخ اعلامیه نباید بعد از تاریخ صورتحساب dateTime باشد. items[].amountAfterDiscount ارزش معامله در اعلامیه فروش بورس به ریال (عدد صحیح بزرگ‌تر از صفر) است و عیناً مبلغ بعد از تخفیف ردیف می‌شود؛ ترتیب اولویت: ابتدا amountAfterDiscount و در نبود آن floor(qty × unitPrice) که باید بزرگ‌تر از صفر باشد. مبلغ واحد در این الگو خارج از الگوست (مبلغ واحد در الگوی ۱۱): unitPrice فقط وقتی لازم است که amountAfterDiscount ارسال نشود، می‌تواند اعشاری باشد، در کنار amountAfterDiscount نادیده گرفته می‌شود ولی در هر حال نباید منفی باشد، و به سامانه مؤدیان ارسال نمی‌شود. مبلغ واحد ذخیره‌شده همیشه از مبلغ بعد از تخفیف محاسبه می‌شود، نه از unitPrice ارسالی؛ برای مثال qty=3 و unitPrice=333333.9 به adis=1000001 و مبلغ واحد نمایشی 333333.7 می‌رسد. فقط یک قلم مجاز است، discount غیرصفر با V79_OUT_OF_PATTERN رد می‌شود و vatRate باید 0 باشد یا ارسال نشود. carat (عیار) اختیاری است و 0 یعنی ارسال‌نشده؛ مقدار دیگر باید بزرگ‌تر از صفر، حداکثر 1000 و حداکثر ۲ رقم اعشار. invoiceSubject=4، contractCode، billNo، tax17، payments، اطلاعات ارزی، سایر مالیات‌ها و contractNo در این الگو پذیرفته نمی‌شوند.
inp=13 بیمهinsPolicyNo, extInsPolicyNoشناسه بیمه‌نامه 9 تا 12 رقم است؛ شناسه الحاقیه اختیاری است.
inp=14 فروش زنجیرهitemsفقط یک قلم کالا یا خدمت مجاز است.

نمونه ورودی ساده آرایه‌ای

salesWithBuyerData request
{
  "uuid": "4d3d2921-6129-4a8b-9dc2-0ef08c2bf4fa",
  "data": [
    {
      "internalId": "INV-10001",
      "no": "A10001",
      "dateTime": "20260705113000",
      "isArticle9": false,
      "invoiceSubject": 1,
      "settlement": 1,
      "send": true,
      "fiscalId": "SND123",
      "sandBox": true,
      "buyer": {
        "type": 2,
        "nationalCode": "10100000000",
        "economicCode": "10987654321000",
        "zipCode": "1234567890",
        "name": "شرکت خریدار نمونه",
        "mobile": "09120000000",
        "address": "تهران، خیابان نمونه، پلاک ۱",
        "phone": "02188770000",
        "branchCode": 1
      },
      "items": [
        {
          "internalId": "ITEM-1001",
          "stuffId": "1234567890123",
          "description": "کالای نمونه",
          "qty": 2,
          "unit": "162",
          "unitPrice": 5000000,
          "discount": 0,
          "vatRate": 10
        }
      ],
      "payments": [
        {
          "type": 2,
          "dateTime": "20260705113000",
          "price": 11000000
        }
      ]
    }
  ]
}

نمونه فیلدهای الگوهای خاص

special template fields
{
  "flightTicket": {
    "endpoint": "/api/invoice/flightTicket",
    "addToEachDataItem": {
      "flightInfo": {
        "flightType": 1,
        "buyerMethod": 1,
        "nationalId": "0012345678",
        "agentEconomicCode": "10987654321000"
      }
    }
  },
  "billOfLading": {
    "endpoint": "/api/invoice/billOfLading",
    "addToEachDataItem": {
      "shipmentInfo": {
        "blNumber": "123456789",
        "originCountry": "364",
        "originCity": "10000",
        "destinationCountry": "364",
        "destinationCity": "20000",
        "senderEconomicCode": "0012345678",
        "receiverEconomicCode": "10987654321",
        "transportType": "1",
        "fleetNumber": "IR-12-345",
        "driverIdCode": "0012345678",
        "totalWeight": 1250,
        "shippedItems": [
          { "stuffId": "1234567890123", "name": "کالای حمل‌شده" }
        ]
      }
    }
  },
  "insurance": {
    "endpoint": "/api/invoice/insurance",
    "typeTwoEndpoint": "/api/invoice/insuranceEndUser",
    "addToEachDataItem": {
      "insPolicyNo": "123456789",
      "extInsPolicyNo": "987654321"
    }
  },
  "oilProducts": {
    "endpoint": "/api/invoice/oilProducts",
    "typeTwoEndpoint": "/api/invoice/oilProductsEndUser"
  },
  "commoditySecurities": {
    "endpoint": "/api/invoice/commoditySecurities",
    "addToEachDataItem": {
      "saleAnnNo": "1405012345",
      "saleAnnDate": "20260705"
    }
  }
}

نمونه کامل مدل ساده برای الگوی بورس (/api/invoice/commoditySecurities)

اصلاحی (invoiceSubject: 2) در این الگو هم تابع وضعیت تأیید اعلامیه فروش بورس است: پس از ثبت «تأیید شده» با /api/invoice/exchangeAnnouncement یا در پنل، اصلاحی با کد EXCHANGE_ANNOUNCEMENT_CONFIRMED در مسیر $.data[i] رد می‌شود و فقط ابطال مجاز است. جزئیات در قاعده اصلاحی الگوی ۱۱.

این نمونه همان مسیر /api/invoice/simple/type/1/template/11 را هم پوشش می‌دهد. saleAnnDate می‌تواند yyyyMMdd، yyyyMMddHHmmss، Unix time یا تاریخ شمسی 1405/04/14 باشد و به همان روز تقویمی ذخیره و ارسال می‌شود. در روش تسویه 3، مقدار payment سهم نقدی است. مطابق نمونه، ارزش معامله در اعلامیه فروش بورس را در amountAfterDiscount بفرستید و unitPrice را حذف کنید؛ مبلغ واحد نمایشی از همین مبلغ محاسبه می‌شود (جدول مبالغ الگوی ۱۱).

commoditySecurities request
{
  "uuid": "0c7f5f0e-8a3d-4a53-9f5c-1d2b3c4d5e6f",
  "data": [
    {
      "internalId": "IME-2001",
      "no": "A20001",
      "dateTime": "20260705113000",
      "invoiceSubject": 1,
      "settlement": 3,
      "payment": 400000,
      "send": false,
      "fiscalId": "SND123",
      "sandBox": true,
      "saleAnnNo": "1405012345",
      "saleAnnDate": "20260705",
      "buyer": { "type": 2, "economicCode": "10987654321000", "zipCode": "1234567890" },
      "items": [
        {
          "stuffId": "1234567890123",
          "description": "کالای معامله‌شده در بورس کالا",
          "qty": 3,
          "unit": "162",
          "amountAfterDiscount": 1000001,
          "vatRate": 0
        }
      ]
    }
  ]
}

نمونه اصلاحی یا ابطالی

amendment/cancel subject
{
  "uuid": "2df1926a-486c-4dd7-9b02-882e1e5a5c4a",
  "data": [
    {
      "internalId": "INV-10001-EDIT",
      "sourceInternalId": "INV-10001",
      "no": "A10002",
      "dateTime": "20260705123000",
      "invoiceSubject": 2,
      "settlement": 1,
      "send": false,
      "fiscalId": "SND123",
      "sandBox": true,
      "buyer": { "type": 2, "economicCode": "10987654321000", "zipCode": "1234567890" },
      "items": [
        { "stuffId": "1234567890123", "description": "کالای اصلاح‌شده", "qty": 2, "unit": "162", "unitPrice": 5200000, "vatRate": 10 }
      ]
    }
  ]
}
نمونه بالا برای الگوهای عمومی است و در الگوی ۱۱ پذیرفته نمی‌شود. برای اصلاحی الگوی ۱۱ در /api/invoice/commoditySecurities، invoiceSubject: 2 و sourceInternalId را همراه saleAnnNo، saleAnnDate و amountAfterDiscount جدید بفرستید؛ unitPrice لازم نیست، vatRate باید 0 باشد یا ارسال نشود و discount غیرصفر پذیرفته نمی‌شود. این اصلاحی فقط تا پیش از تأیید اعلامیه فروش بورس مجاز است (قاعده اصلاحی الگوی ۱۱).
Invoice Actions

ارسال دستی به سازمان امور مالیاتی و ثبت صورتحساب ابطالی

مسیربدنهخروجی
POST /api/invoice/sendInvoice{ "internalIds": ["INV-10001"] }صورتحساب‌های ثبت‌شده به‌صورت دستی در صف ارسال به سازمان امور مالیاتی قرار می‌گیرند. برای هر internalId، آخرین صورتحساب زنجیره آن در نظر گرفته می‌شود؛ یعنی اگر برای آن اصلاحی، ابطالی یا برگشت از فروش ثبت شده باشد (حتی ارسال‌نشده)، همان آخرین صورتحساب در صورت ارسال‌نشده بودن در صف ارسال قرار می‌گیرد و اگر قبلاً ارسال شده باشد فقط وضعیت آن برمی‌گردد. اگر حتی یک شناسه خطا بگیرد، پاسخ با result: 0 فقط errors دارد و data ندارد، هرچند شناسه‌های سالم همان درخواست در صف ارسال قرار گرفته‌اند؛ وضعیت آن‌ها را با سرویس‌های پیگیری بررسی کنید.
POST /api/invoice/cancelInvoiceinternalId مرجع، uuid و در صورت نیاز cancelInternalIdصورتحساب ابطالی ثبت می‌شود؛ مقدار صریح send اولویت دارد و در نبود آن، تنظیم ارسال خودکار «API نسخه ۲» کسب‌وکار اعمال می‌شود. ابطال برای آخرین صورتحساب زنجیره internalId ثبت می‌شود و آن صورتحساب باید ارسال‌شده و دارای شماره مالیاتی باشد؛ اگر آخرین صورتحساب زنجیره یک اصلاحی ارسال‌نشده یا هنوز بی‌نتیجه باشد، درخواست با خطا در مسیر $.internalId رد می‌شود.
cancelInvoice request
{
  "uuid": "74ff263d-bf33-40df-a56d-91f07bb1624b",
  "internalId": "INV-10001",
  "cancelInternalId": "INV-10001-CANCEL",
  "send": true,
  "description": "ابطال به درخواست مشتری"
}
Reference APIs

داده‌های مرجع

مسیراحراز هویتکاربرد
GET /api/InvoiceItemUnitv2.lookup.readدیکشنری UnitId => Name برای انتخاب واحد اندازه‌گیری مجاز.
GET /api/Currencyv2.lookup.readدیکشنری Code => Name برای انتخاب ارزهای مجاز.
Validation Contract

اعتبارسنجی JSON مطابق دستورالعمل

اعتبارسنجی پیش از تبدیل نهایی و ذخیره انجام می‌شود. مسیر خطاها با قراردادی مشابه JSONPath بازگردانده می‌شود؛ برای مثال $.data[1].data.Header.tbill به فیلد tbill در صورتحساب دوم اشاره دارد.

فیلدقاعدهنمونه خطا
$بدنه باید JSON object معتبر باشد. property تکراری پذیرفته نمی‌شود.ساختار JSON معتبر نیست.
uuidدر ریشه درخواست؛ رشته غیرخالی و GUID معتبر. شناسه کل درخواست گروهی است و برای هر درخواست جدید باید مقدار تازه‌ای داشته باشد.این uuid قبلاً در درخواست دیگری از شرکت احراز هویت‌شده ثبت شده است؛ محدوده شرکت فقط برای کنترل تکراری‌بودن استفاده می‌شود.
dataآرایه شامل ۱ تا ۲۵۰ صورتحساب؛ هر عضو مستقل اعتبارسنجی و پردازش می‌شود.بیشتر از ۲۵۰ عضو باعث رد کامل درخواست پیش از پردازش می‌شود.
data[].internalIdرشته غیرخالی، یکتا داخل همان درخواست گروهی و یکتا برای شرکت.internalId ارسالی قبلاً برای این شرکت ثبت شده است.
data[].sendاختیاری و در صورت ارسال باید boolean باشد.true ارسال خودکار و false ثبت بدون ارسال را برای همان عضو قطعی می‌کند؛ در نبود فیلد، تنظیم «API نسخه ۲» کسب‌وکار اعمال می‌شود.
data[].dataobject شامل Header و Body.فیلد data الزامی است.
فیلدقاعدهتوضیح
indatimUnix time عددی بر حسب میلی‌ثانیه، حداکثر 13 رقم.تاریخ و زمان صدور صورتحساب.
indati2mتاریخ و زمان ثبت صورتحساب؛ Unix time عددی بر حسب میلی‌ثانیه، حداکثر ۱۳ رقم و بدون اعشار.فقط در ماده ۹ و همراه insr=1 لازم است؛ باید مستقل از indatim و بزرگ‌تر یا مساوی آن باشد، از زمان ارسال جلوتر نباشد و فاصله آن تا ارسال بیش از ۱۵ روز نباشد.
insrقاعده ارسال صورتحساب؛ در حالت ماده ۹ مقدار عددی 1.برای صورتحساب خارج از مهلت ۱۵ روز همراه indati2m لازم است. برای صورتحساب عادی داخل مهلت، خارج از الگو است و در payload نهایی اعمال نمی‌شود.
inty1 یا 2نوع صورتحساب؛ در صورتحساب ابطالی نسخه ۷.۹ ارسال نمی‌شود و از مرجع دریافت می‌شود.
inp1، 2، 3، 4، 5، 6، 7، 8، 9، 11، 13 یا 14الگوی صورتحساب؛ در صورتحساب ابطالی نسخه ۷.۹ ارسال نمی‌شود و از مرجع دریافت می‌شود.
inty + inpنوع اول: همه الگوهای مجاز؛ نوع دوم: 1، 3، 9، 13ترکیب نوع و الگو پیش از ثبت کنترل می‌شود و برای ترکیب نامعتبر خطای ساخت‌یافته برمی‌گردد.
ins1 اصلی، 2 اصلاحی، 3 ابطالی، 4 برگشت از فروشبرای غیر اصلی، irtaxid الزامی است.
innoرشته شامل حروف و اعداد انگلیسی، حداکثر 10 کاراکتر.سریال داخلی حافظه مالیاتی.
tinsشماره اقتصادی فروشنده، 11 تا 14 رقم.باید با شرکت احراز هویت‌شده برابر باشد.
tob1 حقیقی، 2 حقوقی، 3 مشارکت مدنی، 4 اتباع غیر ایرانینوع شخص خریدار؛ برای صورتحساب نوع دوم و الگوی صادرات اختیاری و برای صورتحساب ابطالی خارج از الگو است.
tinb11 تا 14 رقم.در صورت ارسال خریدار حقوقی الزامی است؛ در الگوی صادرات اختیاری و در صورتحساب ابطالی خارج از الگو است.
bid5 تا 14 رقم.برای نوع اول به‌جز الگوی صادرات، حداقل tinb یا bid لازم است؛ در صورتحساب ابطالی خارج از الگو است.
bpcدر صورت ارسال دقیقاً 10 رقم.کد پستی خریدار.
setm1 نقدی، 2 نسیه، 3 نقدی/نسیهروش تسویه.
capعدد ریالی غیرمنفی.برای روش نقدی و نقدی/نسیه الزامی است. در الگوی بورس inp=11 با روش نقدی/نسیه (setm=3)، همین مقدار سهم نقدی صورتحساب است و ذخیره می‌شود.
inspعدد ریالی غیرمنفی.برای روش نسیه و نقدی/نسیه الزامی است.
tprdis, tdis, tadis, tvam, todam, tbillجمع‌های Header باید با مجموع Body برابر باشند.برای الگوی صادرات برخی جمع‌ها اختیاری‌اند. در الگوی بورس inp=11، tprdis، tdis و todam خارج از الگو هستند و tadis الزامی و برابر adis است.
crnبرای الگوی پیمانکاری الزامی.شماره قرارداد.
billidبرای الگوی قبوض خدماتی الزامی.شناسه قبض.
bpnحداکثر ۹ حرف و رقم لاتین.در الگوی فروش ارز (inp=2) برای خریدار غیرایرانی فاقد کد فراگیر: tob=4 و bpn بدون bid؛ خریدار با همین شماره گذرنامه ثبت می‌شود.
scln, sccscln حداکثر 14 رقم؛ scc دقیقاً 5 رقم.شماره پروانه گمرکی (نوع اول، الگوهای ۱ و ۲) و کد گمرک محل اظهار (نوع اول، الگوهای ۱، ۲ و ۷)؛ ذخیره و به سامانه مؤدیان ارسال می‌شوند. مقدار خارج از این قالب نادیده گرفته می‌شود.
cdcn, cdcdcdcn حداکثر 14 رقم؛ cdcd روز Unix.شماره و تاریخ کوتاژ اظهارنامه گمرکی در الگوی صادرات (inp=7).
in, an9 تا 12 رقم.شناسه یکتای بیمه‌نامه (الزامی) و الحاقیه در الگوی بیمه (inp=13).
Body[].vbaعدد ریالی غیرمنفی.مبلغ پایه مالیات بر ارزش افزوده در الگوی ۹؛ مقدار بزرگ‌تر از صفر ذخیره و ارسال می‌شود.
asnرشته فقط شامل رقم، ۱ تا ۳۰ رقم.شماره اعلامیه فروش بورس؛ در الگوی بورس اوراق بهادار مبتنی بر کالا inp=11 برای صورتحساب اصلی و اصلاحی الزامی است.
asdعدد صحیح مثبت: تعداد روز از 1970/01/01 (Unix day؛ نه میلی‌ثانیه).تاریخ اعلامیه فروش بورس؛ در inp=11 الزامی است و نباید بعد از روز تقویمی تاریخ صدور indatim به وقت تهران باشد. برای نمونه، 20697 یعنی 2026/09/01 (1405/06/10).
inp=11فقط نوع اول، یک ردیف، بدون برگشت از فروش (ins=4).tprdis، tdis و todam خارج از الگو هستند؛ مقدار صفر آن‌ها (و tprdis برابر tadis) حذف و مقدار دیگر رد می‌شود. سایر فیلدهای خارج از الگو (از جمله crn، billid و tax17) با مقدار صفر یا رشته خالی حذف می‌شوند و با مقدار دیگر پذیرفته نمی‌شوند. tadis الزامی است و باید برابر adis تنها ردیف باشد. در setm=3 جمع cap و insp باید برابر tbill باشد. نمونه کامل و قاعده مبلغ واحد در بخش الگوی ۱۱ آمده است.
فیلدقاعدهتوضیح
Bodyآرایه شامل حداقل یک قلم.در الگوی بورس اوراق بهادار مبتنی بر کالا inp=11 و الگوی فروش زنجیره inp=14 فقط یک ردیف مجاز است؛ در صورتحساب ابطالی نسخه ۷.۹ کل Body حذف و اطلاعات از مرجع دریافت می‌شود.
sstidرشته 13 رقمی.شناسه کالا/خدمت.
ssttرشته حداکثر 400 کاراکتر، اختیاری.شرح کالا/خدمت.
muکد عددی واحد اندازه‌گیری.در صورت ارسال باید فقط رقم باشد؛ در الگوی بورس inp=11 الزامی است.
amعدد مثبت.تعداد/مقدار.
feeعدد ریالی صحیح و غیرمنفی (بدون اعشار).مبلغ واحد؛ در همه الگوها به‌جز فروش ارز inp=2 (اختیاری) و بورس inp=11 الزامی است. مقدار اعشاری یا منفی در هر الگویی، حتی وقتی فیلد لازم نیست، خطا می‌گیرد. در الگوی بورس inp=11 خارج از الگوست: ارسال نکنید؛ مقدار معتبر ارسالی نادیده گرفته می‌شود، ذخیره نمی‌شود و به سامانه مؤدیان ارسال نمی‌شود، و با فعال شدن کنترل کامل Rule Matrix نسخه ۷.۹ برای این الگو با V79_OUT_OF_PATTERN رد خواهد شد. جزئیات در مبلغ واحد در الگوی ۱۱.
prdisعدد ریالی غیرمنفی.مبلغ قبل از تخفیف. در الگوی صادرات inp=7 اختیاری است؛ در الگوی بورس inp=11 خارج از الگوست؛ مقدار صفر یا برابر با adis حذف و هر مقدار دیگر رد می‌شود و adis از آن محاسبه نمی‌شود.
disعدد ریالی غیرمنفی، پیش‌فرض صفر.تخفیف. در الگوی بورس inp=11 خارج از الگوست؛ مقدار صفر حذف و مقدار غیرصفر رد می‌شود.
adisبرابر prdis - dis؛ در صورت ارسال‌نشدن از همین رابطه محاسبه می‌شود. در الگوی بورس inp=11 مقدار مستقل و الزامی است و از prdis، dis یا fee محاسبه نمی‌شود.مبلغ بعد از تخفیف. در الگوی بورس inp=11 باید دقیقاً ارزش معامله در اعلامیه فروش بورس (ریال، عدد صحیح بزرگ‌تر از صفر) باشد؛ همین مقدار عیناً ذخیره و ارسال می‌شود و لازم نیست بر am بخش‌پذیر باشد. مبلغ واحد نمایشی ذخیره‌شده از همین مقدار محاسبه می‌شود و ارسال نمی‌شود (مبلغ واحد در الگوی ۱۱).
vraعدد بین 0 تا 100.نرخ مالیات بر ارزش افزوده. در الگوی بورس inp=11 باید 0 باشد.
vamعدد ریالی غیرمنفی.مبلغ مالیات بر ارزش افزوده. در الگوی بورس inp=11 باید 0 باشد.
odam, olamعدد ریالی غیرمنفی؛ در صورت عدم ارسال، صفر در نظر گرفته می‌شود.سایر مالیات/عوارض و وجوه قانونی. در الگوی بورس inp=11 خارج از الگوست؛ مقدار صفر حذف و مقدار غیرصفر رد می‌شود.
tsstamبرابر adis + vam + odam + olam.مبلغ کل ردیف.
cfee, cut, exrاگر اطلاعات ارزی ارسال شود، کنترل می‌شوند.میزان ارز، نوع ارز و نرخ برابری. در الگوی بورس inp=11 خارج از الگوست؛ مقدار صفر یا خالی حذف و مقدار دیگر رد می‌شود.
consfee, spro, bros, tcpbsبرای طلا/جواهر inp=3 الزامی است.اجرت، سود، حق‌العمل و جمع آن‌ها.
nw, ssrv, sscvبرای صادرات inp=7 الزامی.وزن خالص، ارزش ریالی و ارزش ارزی.
cuiاختیاری؛ بزرگ‌تر از صفر، حداکثر 1000 و حداکثر ۲ رقم اعشار.عیار در الگوی بورس inp=11؛ مقدار 0 یعنی ارسال‌نشده و حذف می‌شود.
فیلدقاعدهتوضیح
Paymentsاختیاری؛ در صورت ارسال باید array باشد.جزئیات پرداخت. در الگوی بورس inp=11 خارج از الگوست؛ فیلد را حذف کنید یا آرایه خالی بفرستید.
pmtعدد صحیح یکی از مقادیر 1 تا 8.1 چک، 2 تهاتر، 3 وجه نقد، 4 کارت‌خوان (POS)، 5 درگاه پرداخت اینترنتی، 6 کارت‌به‌کارت، 7 انتقال به حساب، 8 سایر.
pdtUnix time عددی بر حسب میلی‌ثانیه.تاریخ پرداخت.
pvعدد ریالی غیرمنفی.نباید از tbill بیشتر شود.
acn14 رقم.برای pmt=4 الزامی.
trmn8 رقم.برای pmt=4 الزامی.
pcn16 رقم.برای pmt=4 الزامی.
iinn9 رقم.شماره سوئیچ پرداخت، اختیاری.
trn1 تا 14 رقم.شماره پیگیری یا مرجع، اختیاری.
pid1 تا 12 رقم.شناسه پرداخت‌کننده، اختیاری.
کنترلقاعدهمسیر خطا
مالکیت فروشندهHeader.tins برابر کد اقتصادی شرکت صاحب کلید.$.data[n].data.Header.tins
جمع قبل از تخفیفHeader.tprdis = sum(Body[].prdis)$.data[n].data.Header.tprdis
جمع تخفیفHeader.tdis = sum(Body[].dis)$.data[n].data.Header.tdis
جمع بعد از تخفیفHeader.tadis = sum(Body[].adis)$.data[n].data.Header.tadis
جمع ارزش افزودهHeader.tvam = sum(Body[].vam)$.data[n].data.Header.tvam
جمع کلHeader.tbill = sum(Body[].tsstam)$.data[n].data.Header.tbill
تسویهcap + insp <= tbill در صورت ارسال cap یا insp.$.data[n].data.Header.cap
پرداخت‌هامجموع Payments[].pv نباید از tbill بیشتر باشد.$.data[n].data.Payments
Schema Notes

نکات مهم ساختار داده

موضوعتوضیحپیشنهاد پیاده‌سازی
مبالغ ریالیفیلدهای مبلغی باید به‌صورت عدد غیرمنفی و بدون اعشار ارسال شوند.محاسبات را پیش از serialization نهایی گرد و کنترل کنید.
تاریخ‌هاindatim، indati2m و pdt Unix time بر حسب میلی‌ثانیه هستند. indati2m فقط تاریخ ثبت ماده ۹ است و جایگزین indatim نمی‌شود.ارسال timestamp ثانیه‌ای باعث خطا می‌شود.
حساسیت نام فیلدهانام فیلدها در سمت API به‌صورت case-insensitive پردازش می‌شوند.برای هماهنگی با نمونه‌ها از Header، Body و Payments استفاده کنید.
ارسال خودکارمقدار صریح send برای هر صورتحساب بر تنظیم کسب‌وکار اولویت دارد؛ در نبود آن، تنظیم مستقل «API نسخه ۲» استفاده می‌شود.برای رفتار قطعی همان درخواست، مقدار send: true یا send: false را ارسال کنید.
درخواست گروهیهر عضو data مستقل اعتبارسنجی و ذخیره می‌شود و پاسخ ۱:۱ به ترتیب ورودی برمی‌گردد. سقف هر درخواست ۲۵۰ عضو است.فقط اعضای دارای status: 3 را پس از اصلاح و با uuid جدید دوباره ارسال کنید.
Errors

خطاها و پاسخ‌ها

در خطاهای قابل کنترل، API پاسخ JSON شامل result، traceId، message و در صورت وجود errors[] بازمی‌گرداند. مقدار traceId را برای پیگیری پشتیبانی نگه‌داری کنید.

هر صورتحساب هر درخواست ثبت (و هر ابطال) یک ردیف در صفحه «لاگ‌های برنامه» پنل معتمد آسا با بخش «API نسخه ۲» دارد: نتیجه (موفق/ناموفق)، متن خطای همان صورتحساب، شماره صورتحساب، internalId و traceId در آن ثبت می‌شود و با جست‌وجوی هر کدام از این مقادیر پیدا می‌شود. برای دیدن علت رد یک صورتحساب، پیش از تماس با پشتیبانی همین صفحه را ببینید.
resultمعنانمونه وضعیت
-2درخواست تکراریuuid این درخواست قبلاً در محدوده شرکت احراز هویت‌شده ثبت شده و کل درخواست رد شده است.
-1احراز هویت نامعتبرهدر Authorization ارسال نشده، Access Token منقضی یا scope لازم وجود ندارد.
0هیچ صورتحسابی ثبت نشدهساختار ریشه یا uuid نامعتبر است، تعداد اعضا بیشتر از ۲۵۰ است، یا تمام اعضای آرایه خطا دارند.
1حداقل یک ثبت موفقممکن است همه اعضا موفق باشند یا پاسخ جزئی شامل اعضای موفق و اعضای دارای status: 3 باشد.
2، 3ثبت مشتریexists یا edit در پاسخ سرویس ثبت مشتری.

کدهای خطای اعلامیه فروش بورس (الگوی ۱۱)

این کدها در errors[].code برمی‌گردند. پاسخ HTTP مانند سایر سرویس‌ها 200 است؛ در سرویس‌های وضعیت اعلامیه، result برابر 0 است و در ثبت گروهی فقط همان عضو خطا می‌گیرد. سایر خطاها بدون code و مانند گذشته برمی‌گردند.

codepathمعنا و اقدام
EXCHANGE_ANNOUNCEMENT_CONFIRMED$.data[i] یا در sendInvoice مسیر $.internalIds[i]اعلامیه فروش بورس این زنجیره تأیید شده است؛ اصلاحی مجاز نیست و فقط ابطال امکان‌پذیر است. پیش‌نویس اصلاحی ثبت‌شده هم تا وقتی وضعیت «تأیید شده» است ارسال نمی‌شود.
EXCHANGE_ANNOUNCEMENT_STATUS_UNAVAILABLE$.data[i]، $.internalIds[i] یا $وضعیت تأیید اعلامیه موقتاً قابل خواندن نیست؛ اصلاحی الگوی ۱۱ فعلاً ممکن نیست. چند دقیقه بعد دوباره تلاش کنید. سایر صورتحساب‌ها تحت تأثیر نیستند.
INVOICE_NOT_FOUND$.internalId یا $.taxIdصورتحسابی با این شناسه در شرکت احراز هویت‌شده یافت نشد.
EXCHANGE_ANNOUNCEMENT_NOT_APPLICABLE$صورتحساب اصلی زنجیره نوع اول با الگوی ۱۱ نیست، هنوز با موفقیت ارسال نشده، یا صورتحساب درخواستی ابطالی است.
EXCHANGE_ANNOUNCEMENT_FORBIDDEN$وضعیت از سامانه سازمان (source: orgApi) دریافت شده است و فقط مدیر سامانه می‌تواند آن را به‌صورت دستی تغییر دهد.
EXCHANGE_ANNOUNCEMENT_NOTE_REQUIRED$.noteبرای تغییر دستی وضعیتی که از سامانه سازمان دریافت شده، ثبت توضیح الزامی است.
EXCHANGE_ANNOUNCEMENT_VERSION_CONFLICT$.expectedVersionوضعیت هم‌زمان تغییر کرده است؛ دوباره استعلام کنید و با version جدید ارسال کنید.
EXCHANGE_ANNOUNCEMENT_REQUEST_INVALID$، $.confirmed، $.note یا $.expectedVersionدرخواست نامعتبر است: هیچ‌یک یا هر دو فیلد internalId و taxId ارسال شده، confirmed ارسال نشده، توضیح بیش از ۵۰۰ کاراکتر است یا expectedVersion معتبر نیست.
INVOICE_PAYMENT_NOT_APPLICABLE$.internalIdروی این صورتحساب پرداخت در کارپوشه ثبت نمی‌شود: ارسال‌نشده یا بی‌نتیجه، ابطالی، الگوی بورس، یا روش تسویه نقدی. کد دلیل در field (مانند CASH_SETTLEMENT، INVOICE_NOT_APPROVED) و توضیح در message.
INVOICE_PAYMENT_DATE_BEFORE_ISSUE$.payments[i].dateTimeتاریخ و زمان پرداخت قبل از تاریخ و زمان صدور صورتحساب است؛ سازمان چنین پرداختی را نمی‌پذیرد (کد رسمی 10014).
INVOICE_PAYMENT_DATE_IN_FUTURE$.payments[i].dateTimeتاریخ و زمان پرداخت بعد از زمان حال (به وقت تهران، با چند دقیقه اغماض) است؛ پرداختی که هنوز انجام نشده ثبت نمی‌شود (کد رسمی 10013).
INVOICE_PAYMENT_ROW_LOCKED$.paymentsردیفی که سازمان دارد تغییر کرده یا حذف شده است؛ فقط افزودن پرداخت جدید ممکن است.
INVOICE_PAYMENT_DUPLICATE_REQUEST$.uuiduuid قبلاً برای این شرکت استفاده شده است (result: -2)؛ وضعیت را با inquiryPayment ببینید و با uuid تازه فقط پرداخت‌های ثبت‌نشده را بفرستید.
INVOICE_PAYMENT_REGISTRATION_DISABLED$ثبت پرداخت در کارپوشه موقتاً در سامانه غیرفعال است.
INVOICE_PAYMENT_INTERNAL_ERROR$خطای داخلی؛ با traceId به پشتیبانی مراجعه کنید.

نمونه پاسخ جزئی: یک ثبت موفق و یک خطا

Partial batch response
{
  "result": 1,
  "traceId": "d36131a5-11e0-402b-9b0b-4a921333c317",
  "message": "1 صورتحساب ثبت شد و 1 مورد دارای خطا بود.",
  "errors": [
    {
      "path": "$.data[1].data.Header.tbill",
      "message": "مجموع صورتحساب باید با جمع مقادیر متناظر در Body برابر باشد. مقدار صحیح: 5500000"
    }
  ],
  "data": [
    {
      "asatspId": 105234,
      "status": 0,
      "issue": 1,
      "internalId": "1000001"
    },
    {
      "status": 3,
      "error": [
        "$.data[1].data.Header.tbill: مجموع صورتحساب باید با جمع مقادیر متناظر در Body برابر باشد. مقدار صحیح: 5500000"
      ],
      "internalId": "1000002"
    }
  ]
}

نمونه رد کامل به‌دلیل عبور از سقف batch

Batch limit error
{
  "result": 0,
  "traceId": "17f35c78-58b2-4f8f-8d5d-5e1ea281c1af",
  "message": "ساختار کلی درخواست معتبر نیست. تعداد خطا: 1",
  "errors": [
    {
      "path": "$.data",
      "message": "آرایه data نمی‌تواند بیشتر از 250 صورتحساب داشته باشد."
    }
  ]
}

نمونه پاسخ موفق ثبت صورتحساب

Send success response
{
  "result": 1,
  "traceId": "5f1ca11c-56d0-4420-8f72-7d0ef41751f1",
  "message": "صورتحساب‌ها با موفقیت ثبت شدند.",
  "data": [
    {
      "asatspId": 105234,
      "status": 0,
      "issue": 1,
      "internalId": "1000001"
    },
    {
      "asatspId": 105235,
      "status": 0,
      "issue": 1,
      "internalId": "1000002"
    }
  ]
}
Inquiry

پیگیری وضعیت صورتحساب

سرویس‌های پیگیری وضعیت با Access Token دارای scope v2.invoice.read فراخوانی می‌شوند و آخرین وضعیت پردازش صورتحساب را بازمی‌گردانند. در دوره مهاجرت، Customer API Key قدیمی هم پذیرفته می‌شود.

برای هر سه روش پیگیری، مسیر تک‌مقداری و مسیر آرایه‌ای مستقل وجود دارد. مسیرهای آرایه‌ای حداکثر ۲۵۰ مقدار می‌پذیرند و پاسخ را دقیقاً با ترتیب ورودی برمی‌گردانند؛ بنابراین نتیجه یا خطای هر عضو با همان index ورودی متناظر است.

مسیرفیلد درخواستکاربرد
POST /api/invoice/inquiryInternalIdinternalIdپیگیری با کد داخلی ارسالی.
POST /api/invoice/inquiryInternalIdsinternalIds[]پیگیری گروهی با کدهای داخلی؛ حداکثر ۲۵۰ مقدار.
POST /api/invoice/inquiryNonoپیگیری با شماره صورتحساب یا inno.
POST /api/invoice/inquiryNosnos[]پیگیری گروهی با شماره‌های صورتحساب یا inno؛ حداکثر ۲۵۰ مقدار.
POST /api/invoice/inquiryTaxIdtaxIdپیگیری با شماره منحصر به فرد مالیاتی.
POST /api/invoice/inquiryTaxIdstaxIds[]پیگیری گروهی با شماره‌های منحصر به فرد مالیاتی؛ حداکثر ۲۵۰ مقدار.
inquiryInternalId body
{
  "internalId": "1000001"
}
inquiryInternalIds body
{
  "internalIds": [
    "1000001",
    "1000002"
  ]
}
inquiryNo body
{
  "no": "A000000001"
}
inquiryNos body
{
  "nos": [
    "A000000001",
    "A000000002"
  ]
}
inquiryTaxId body
{
  "taxId": "A1B2C3D4E5F6G7H8I9J0K1"
}
inquiryTaxIds body
{
  "taxIds": [
    "A1B2C304E7900000F42410",
    "SND12304E7900000F42411"
  ]
}
cURL / batch inquiry
curl -X POST "https://api-v2.asatsp.ir/api/invoice/inquiryInternalIds" \
  -H "Authorization: Bearer <AccessToken with v2.invoice.send>" \
  -H "Content-Type: application/json; charset=utf-8" \
  -d '{"internalIds":["1000001","1000002"]}'

نمونه پاسخ آرایه‌ای

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

inquiryInternalIds response
[
  {
    "asatspId": 105234,
    "taxId": "A1B2C304E7900000F42410",
    "status": 2,
    "internalId": "1000001"
  },
  {
    "status": 3,
    "error": ["صورتحساب یافت نشد"],
    "internalId": "1000002"
  }
]

وضعیت‌های رایج

statusمعنااقدام پیشنهادی
0در صف ارسالپس از چند دقیقه، وضعیت را دوباره پیگیری کنید.
1ارسال‌شده و در انتظار پاسخ سامانهپس از چند دقیقه، وضعیت را دوباره پیگیری کنید.
2موفقشماره مرجع و taxId را ذخیره کنید.
3دارای خطاپیام خطا را در error بررسی کنید؛ در این وضعیت json برنمی‌گردد.
refrenceNumber (شماره مرجع سامانه مؤدیان) فقط در وضعیت‌های 1 و 2 برمی‌گردد (در وضعیت 1 ممکن است هنوز خالی باشد) و در وضعیت‌های 0 و 3 نمی‌آید. فقط در وضعیت 2 (موفق)، json (همان payload ارسال‌شده به سامانه مؤدیان) و در صورت وجود مقدار، statusInquery (وضعیت صورتحساب/واکنش خریدار در سامانه مؤدیان) و article6Status (وضعیت ماده ۶: عدول یا عدم عدول) هم برمی‌گردند. پاسخ پیگیری فیلد جداگانه‌ای برای ردیف‌ها ندارد. در الگوی ۱۱، در json.Body[0] adis و am مقدار دارند ولی fee، prdis و dis مقداری ندارند (برابر null هستند یا اصلاً نمی‌آیند)؛ مبلغ واحد نمایشی ذخیره‌شده در پاسخ پیگیری برنمی‌گردد (مبلغ واحد در الگوی ۱۱).
Commodity Exchange / Pattern 11

وضعیت تأیید اعلامیه فروش بورس (الگوی ۱۱)

طبق دستورالعمل RC_IITP.IS_V7.9، برای صورتحساب نوع اول با الگوی بورس اوراق بهادار مبتنی بر کالا (inp=11)، صدور اصلاحی (ins=2) فقط تا زمانی مجاز است که اعلامیه فروش بورس تأیید نشده باشد؛ پس از تأیید فقط ابطال (ins=3) مجاز است. سامانه مؤدیان راهی برای استعلام این تأیید ندارد، بنابراین وضعیت هر زنجیره صورتحساب به‌صورت دستی (از پنل یا همین سرویس‌ها) ثبت می‌شود و همه تصمیم‌های اصلاحی آن زنجیره در پنل و API از همین وضعیت پیروی می‌کنند.

مسیرscopeبدنهکاربرد
POST /api/invoice/inquiryExchangeAnnouncementv2.invoice.readinternalId یا taxIdاستعلام وضعیت فعلی زنجیره.
POST /api/invoice/exchangeAnnouncementv2.invoice.sendinternalId یا taxId، confirmed، note، expectedVersionثبت دستی «تأیید شده» یا «تأیید نشده».
وضعیت متعلق به زنجیره است

هر internalId یا taxId از زنجیره (اصلی یا اصلاحی) را می‌توانید بفرستید؛ وضعیت یک بار برای کل زنجیره ذخیره می‌شود و کلید آن originTaxId (شماره مالیاتی صورتحساب اصلی زنجیره) است. صورتحساب فقط در محدوده شرکت احراز هویت‌شده جست‌وجو می‌شود.

پیش‌شرط

صورتحساب اصلی زنجیره باید نوع اول، الگوی ۱۱ و با موفقیت ارسال‌شده باشد. برای صورتحساب ابطالی وضعیت ثبت نمی‌شود. در غیر این صورت خطای EXCHANGE_ANNOUNCEMENT_NOT_APPLICABLE برمی‌گردد.

تکرار بی‌اثر است

ارسال دوباره همان وضعیت (و همان note یا بدون note) چیزی ثبت نمی‌کند و پاسخ موفق با وضعیت فعلی برمی‌گرداند؛ بنابراین تلاش مجدد پس از قطع ارتباط امن است.

کنترل تغییر هم‌زمان

expectedVersion اختیاری است. اگر مقدار version آخرین استعلام را بفرستید و وضعیت در این فاصله تغییر کرده باشد، خطای EXCHANGE_ANNOUNCEMENT_VERSION_CONFLICT برمی‌گردد.

فیلدهای درخواست

فیلدقاعدهتوضیح
tokenاختیاریتوکن تکمیلی نمایندگی، مانند سایر سرویس‌ها.
internalIdدقیقاً یکی از internalId یا taxIdکد داخلی صورتحساب.
taxIdدقیقاً یکی از internalId یا taxIdشماره منحصر به فرد مالیاتی صورتحساب.
confirmedفقط در exchangeAnnouncement، الزامی، true یا falsetrue یعنی اعلامیه فروش بورس تأیید شده و false یعنی تأیید نشده است.
noteاختیاری، حداکثر ۵۰۰ کاراکترتوضیح تغییر؛ در تاریخچه ثبت می‌شود.
expectedVersionاختیاریهمان version پاسخ استعلام.

مقادیر وضعیت

statusمعنااثر بر اصلاحی
notRecordedهنوز وضعیتی ثبت نشده است.اصلاحی مانند گذشته مجاز است.
notConfirmedاعلامیه فروش بورس تأیید نشده است.اصلاحی مجاز است.
confirmedاعلامیه فروش بورس تأیید شده است.فقط ابطال؛ اصلاحی با EXCHANGE_ANNOUNCEMENT_CONFIRMED رد می‌شود.

منبع و کانال ثبت

فیلدمقدارمعنا
sourcemanualثبت دستی (پنل، API نسخه ۲ یا اسکریپت پشتیبانی).
sourceorgApiدریافت‌شده از API آتی سازمان. تغییر دستی آن فقط برای مدیر سامانه و با توضیح مجاز است؛ در API نسخه ۲ خطای EXCHANGE_ANNOUNCEMENT_FORBIDDEN برمی‌گردد.
channelpanel، apiV2، orgApi، scriptمسیری که آخرین ثبت از آن انجام شده است. ثبت از این سرویس همیشه apiV2 است و setBy شناسه client یا legacy-company:{companyId} را نشان می‌دهد.

فیلدهای پاسخ

پاسخ در قالب همیشگی (result، traceId، message، errors، data) با HTTP 200 برمی‌گردد و وضعیت در فیلد exchangeAnnouncement قرار دارد. در خطا، result برابر 0 (و در خطای احراز هویت -1) است و کد خطا در errors[].code می‌آید؛ فهرست کدها در جدول خطاها.

فیلدتوضیح
invoiceIdشناسه صورتحساب درخواستی.
originTaxId، rootInvoiceIdشماره مالیاتی و شناسه صورتحساب اصلی زنجیره (کلید وضعیت).
statusnotRecorded، notConfirmed یا confirmed.
source، channelمنبع و کانال آخرین ثبت؛ پیش از اولین ثبت وجود ندارند.
setBy، setByUserId، setAtUtc، setAtثبت‌کننده و زمان آخرین ثبت (setAt به تاریخ شمسی و وقت تهران).
saleAnnNo، saleAnnDateشماره و تاریخ شمسی اعلامیه فروش بورس زنجیره.
noteتوضیح آخرین ثبت.
versionنسخه ردیف وضعیت؛ برای expectedVersion.
canChangeآیا این توکن می‌تواند وضعیت را تغییر دهد (نیازمند v2.invoice.send و منبع غیر orgApi).
pendingCorrectionDraftsاصلاحی‌های باز زنجیره (invoiceId، no، hash): پیش‌نویس‌های ارسال‌نشده، و اصلاحی‌هایی که برای ارسال ثبت شده‌اند ولی هنوز نتیجه‌ای از سامانه مؤدیان ندارند با inFlight: true. برای پیش‌نویس ارسال‌نشده فیلد inFlight وجود ندارد.
ثبت «تأیید شده» حتی وقتی پیش‌نویس اصلاحی ارسال‌نشده دارید مجاز است و آن پیش‌نویس‌ها در pendingCorrectionDrafts فهرست می‌شوند. تا وقتی وضعیت «تأیید شده» است، این پیش‌نویس‌ها نه ذخیره می‌شوند و نه ارسال؛ برای مثال /api/invoice/sendInvoice برای آن‌ها خطای EXCHANGE_ANNOUNCEMENT_CONFIRMED در مسیر $.internalIds[i] برمی‌گرداند. اصلاحی‌ای که پیش از ثبت «تأیید شده» برای ارسال ثبت شده ولی هنوز نتیجه‌ای ندارد با inFlight: true در همین فهرست می‌آید؛ تا وقتی وضعیت «تأیید شده» است، سامانه آن را دوباره در صف ارسال قرار نمی‌دهد.
پیش‌نویس اصلاحی پس از «تأیید شده»: اصلاحی ثبت‌شده، حتی اگر ارسال نشده باشد، بلافاصله آخرین صورتحساب زنجیره می‌شود و sendInvoice و cancelInvoice همیشه روی آخرین صورتحساب زنجیره عمل می‌کنند. بنابراین:
  • sendInvoice با internalId صورتحساب اصلی یا هر عضو قبلی زنجیره، همان پیش‌نویس اصلاحی را ارسال می‌کند و در وضعیت «تأیید شده» با EXCHANGE_ANNOUNCEMENT_CONFIRMED در مسیر $.internalIds[i] رد می‌شود. چون با هر خطا پاسخ result: 0 بدون data برمی‌گردد، سایر شناسه‌های همان درخواست را که ممکن است در صف ارسال قرار گرفته باشند با سرویس‌های پیگیری بررسی کنید.
  • cancelInvoice فقط آخرین صورتحساب زنجیره را، در صورتی که ارسال‌شده و دارای شماره مالیاتی باشد، باطل می‌کند؛ تا وقتی پیش‌نویس اصلاحی ارسال‌نشده آخرین صورتحساب زنجیره است، ابطال با خطا در مسیر $.internalId رد می‌شود.
  • API نسخه ۲ امکان حذف پیش‌نویس ارسال‌نشده را ندارد. برای ابطال زنجیره، ابتدا پیش‌نویس را در پنل حذف کنید تا صورتحساب قبلی دوباره آخرین صورتحساب زنجیره شود، سپس cancelInvoice را فراخوانی کنید.
  • اگر «تأیید شده» به اشتباه ثبت شده است، ثبت confirmed: false اصلاحی‌ها و پیش‌نویس‌های همان زنجیره را دوباره قابل ثبت و ارسال می‌کند. این تغییر برای وضعیت دریافت‌شده از سامانه سازمان (source: orgApi) با EXCHANGE_ANNOUNCEMENT_FORBIDDEN رد می‌شود.
اگر وضعیت موقتاً قابل خواندن نباشد، فقط اصلاحی‌های زنجیره الگوی ۱۱ با کد EXCHANGE_ANNOUNCEMENT_STATUS_UNAVAILABLE متوقف می‌شوند؛ صورتحساب اصلی، ابطال، برگشت از فروش و صورتحساب‌های سایر الگوها تحت تأثیر نیستند.
در پاسخ سرویس‌های پیگیری (inquiryInternalId، inquiryNo، inquiryTaxId و نسخه‌های آرایه‌ای)، پاسخ ثبت و پاسخ sendInvoice و cancelInvoice، برای صورتحساب الگوی ۱۱ فیلد اختیاری exchangeAnnouncement با ساختار کوتاه { status, source?, setBy?, setAt?, setAtUtc? } اضافه می‌شود. برای سایر الگوها، یا اگر وضعیت قابل خواندن نباشد، این فیلد در پاسخ وجود ندارد.
inquiryExchangeAnnouncement body
{
  "taxId": "A1B2C304E7900000F42410"
}
exchangeAnnouncement body
{
  "internalId": "IME-1001",
  "confirmed": true,
  "note": "اعلامیه در سامانه بورس کالا تأیید شد.",
  "expectedVersion": "AAAAAAAAB9A="
}
exchangeAnnouncement response
{
  "result": 1,
  "traceId": "0c5d2a4e-6f1b-4c3e-9a57-2b8f4d1e7a90",
  "message": "وضعیت اعلامیه فروش بورس ثبت شد.",
  "data": [],
  "exchangeAnnouncement": {
    "invoiceId": 105234,
    "originTaxId": "A1B2C304E7900000F42410",
    "rootInvoiceId": 105234,
    "status": "confirmed",
    "source": "manual",
    "channel": "apiV2",
    "setBy": "API v2 (customer:81001)",
    "setAtUtc": "2026-09-15T06:30:00Z",
    "setAt": "1405/06/24 ساعت 10:00",
    "saleAnnNo": "1405012345",
    "saleAnnDate": "1405/06/10",
    "note": "اعلامیه در سامانه بورس کالا تأیید شد.",
    "version": "AAAAAAAAB9E=",
    "canChange": true,
    "pendingCorrectionDrafts": [
      { "invoiceId": 105240, "no": "A000000012", "hash": "9b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e" },
      { "invoiceId": 105241, "no": "A000000013", "hash": "0a1b2c3d4e5f60718293a4b5c6d7e8f9", "inFlight": true }
    ]
  }
}
Invoice Payment / کارپوشه

ثبت پرداخت صورتحساب ارسال‌شده در کارپوشه

برای صورتحسابی که با روش تسویه نسیه یا نقدی/نسیه به سامانه مؤدیان ارسال و در کارپوشه ثبت شده است، پرداخت‌های بعدی خریدار از طریق سرویس رسمی «ثبت پرداخت صورتحساب» (invoice-payment، مستند RC_TICS.IS) در کارپوشه ثبت می‌شوند. این سرویس همان کار را برای شما انجام می‌دهد: پرداخت‌های جدید روی آخرین صورتحساب زنجیره internalId ذخیره می‌شوند، نسخهٔ جدید صورتحساب با همان شماره مالیاتی ساخته می‌شود (اصلاحی صادر نمی‌شود) و هر ردیف جدید در همان درخواست به سامانه مؤدیان فرستاده می‌شود. اگر پاسخ سازمان به موقع نرسد یا خطای گذرا (مانند کد 10015) برگردد، ثبت به‌صورت خودکار دوباره تلاش می‌شود و نتیجه را با /api/invoice/inquiryPayment پیگیری می‌کنید.

مسیرscopeبدنهکاربرد
POST /api/invoice/registerPaymentv2.invoice.senduuid، internalId، payments[]افزودن پرداخت‌های جدید به صورتحساب ارسال‌شده و ثبت آن‌ها در کارپوشه.
POST /api/invoice/inquiryPaymentv2.invoice.readinternalIdوضعیت همهٔ ردیف‌های پرداخت صورتحساب و وضعیت ثبت هر کدام در کارپوشه.
پیش‌شرط صورتحساب

آخرین صورتحساب زنجیره باید ارسال‌شده و در سامانه مؤدیان ثبت شده باشد (وضعیت تأیید شده، تأیید سیستمی یا در انتظار واکنش)، ابطالی نباشد و روش تسویه‌ای که به سازمان ارسال شده نقدی نباشد؛ الگوی بورس (inp=11) اطلاعات پرداخت ندارد. روش تسویهٔ صورتحساب با ثبت پرداخت تغییر نمی‌کند (همان است که به سازمان رفته). در غیر این صورت خطای INVOICE_PAYMENT_NOT_APPLICABLE با دلیل در errors[].message و کد دلیل در errors[].field برمی‌گردد و چیزی ذخیره نمی‌شود.

تاریخ پرداخت

تاریخ و زمان هر پرداخت نباید قبل از تاریخ و زمان صدور صورتحساب باشد (خطای INVOICE_PAYMENT_DATE_BEFORE_ISSUE در مسیر $.payments[i].dateTime) و نباید بعد از زمان حال باشد (خطای INVOICE_PAYMENT_DATE_IN_FUTURE در همان مسیر، معادل کد رسمی 10013؛ مقایسه به وقت تهران و با چند دقیقه اغماض برای اختلاف ساعت). سازمان همچنین پرداخت خارج از دورهٔ جاری را نمی‌پذیرد (کد رسمی 10012)؛ این یکی در نتیجهٔ ثبت همان ردیف با وضعیت rejected برمی‌گردد.

ردیف‌های قبلی قفل هستند

ردیفی که سازمان دارد (همراه صورتحساب رفته یا با این سرویس ثبت شده) دیگر قابل ویرایش یا حذف نیست، چون سامانه مؤدیان سرویسی برای حذف یا اصلاح پرداخت ندارد. این سرویس فقط پرداخت اضافه می‌کند؛ در پاسخ، payments[].locked این ردیف‌ها را نشان می‌دهد.

تکرار درخواست

uuid برای هر درخواست الزامی و در محدودهٔ شرکت یکتاست؛ ارسال دوبارهٔ همان uuid با result: -2 و کد INVOICE_PAYMENT_DUPLICATE_REQUEST رد می‌شود تا یک پرداخت دو بار ثبت نشود. پس از قطع ارتباط، ابتدا با inquiryPayment وضعیت را ببینید و فقط پرداخت‌هایی را که در فهرست نیستند با uuid تازه بفرستید.

فیلدهای درخواست registerPayment

فیلدقاعدهتوضیح
tokenاختیاریتوکن تکمیلی نمایندگی، مانند سایر سرویس‌ها.
uuidالزامی، GUID یکتا برای شرکتشناسهٔ این درخواست؛ برای کنترل تکراری‌بودن.
internalIdالزامیکد داخلی صورتحساب؛ آخرین صورتحساب زنجیرهٔ آن در نظر گرفته می‌شود.
paymentsآرایهٔ ۱ تا ۵۰ عضوهر عضو با همان مدل payments[] مدل ساده (بخش ۰۶).
payments[].typeالزامی، 1 تا 8همان کدهای روش پرداخت مدل ساده (جدول مقادیر شمارشی)؛ به ترتیب به paymentMethod سرویس سازمان (CHEQUE، BARTER، CASH، POS، INTERNET، CARD، TRANSFER، OTHER) نگاشت می‌شود.
payments[].dateTimeالزامی؛ Unix time میلی‌ثانیه، yyyyMMddHHmmss یا تاریخ شمسی 1405/06/25 10:30تاریخ و زمان پرداخت؛ نباید قبل از صدور صورتحساب باشد.
payments[].priceالزامی، عدد ریالی بزرگ‌تر از صفر، حداکثر ۱۸ رقممبلغ پرداخت همان‌طور که خریدار پرداخته (با مالیات بر ارزش افزوده)؛ اعشار حذف می‌شود. آنچه به کارپوشه می‌رود (paidAmount) سهم بدون مالیات همین مبلغ است: کارپوشه مبالغ تسویه را بدون مالیات نگه می‌دارد و «سهم مالیات بر ارزش افزوده از پرداخت» را خودش می‌افزاید؛ سهم مالیات با نسبت مالیات به مبلغ نهایی همان صورتحساب محاسبه و از مبلغ کم می‌شود (مثلاً روی صورتحساب ۱۰٪، پرداخت ۳٬۲۴۲٬۱۷۰ ریال با paidAmount برابر ۲٬۹۴۷٬۴۲۸ ثبت می‌شود). مبلغ ثبت‌شده در payments[].registration.registeredAmount برمی‌گردد.
payments[].terminalNumberاختیاری، فقط رقم، حداکثر ۸ رقمشماره پایانه (terminalNumber).
payments[].traceNoاختیاری، فقط رقم، حداکثر ۱۴ رقمشماره پیگیری یا مرجع پرداخت (referenceNumber).
payments[].cardNo، payerNationalCode، switchNumber، acquirerNumber، descriptionاختیاری (رقم، به ترتیب حداکثر ۱۶، ۱۲، ۹ و ۱۴ رقم)در سوابق شما ذخیره می‌شوند و به سرویس ثبت پرداخت سازمان فرستاده نمی‌شوند.
registerPayment request
{
  "uuid": "1f0c0f1e-6b1c-4c7b-9a2e-4d5a9d2f7a11",
  "internalId": "INV-10001",
  "payments": [
    {
      "type": 6,
      "dateTime": "1405/06/25 10:30",
      "price": 25000000,
      "traceNo": "13023878656431",
      "terminalNumber": "1302387",
      "description": "قسط دوم"
    }
  ]
}

فیلدهای پاسخ

پاسخ در قالب همیشگی (result، traceId، message، errors، data) با HTTP 200 برمی‌گردد. عضو data[0] همان اطلاعات استعلام صورتحساب است به‌علاوهٔ paymentRegistration (قابلیت ثبت پرداخت روی این صورتحساب) و payments[] (همهٔ ردیف‌های پرداخت صورتحساب با وضعیت هر کدام). در خطا، result برابر 0 (تکراری -2، احراز هویت -1) است و کدها در errors[].code می‌آیند.

فیلدتوضیح
paymentRegistration.eligibletrue یعنی پرداخت‌های جدید روی این صورتحساب در کارپوشه ثبت می‌شوند؛ در غیر این صورت code و reason دلیل را می‌گویند.
paymentRegistration.rowsLockedصورتحساب ارسال‌شده است و ردیف‌های قبلی پرداخت آن قابل تغییر نیستند.
payments[].id، type، dateTime، timestamp، price، traceNo، terminalNumberردیف پرداخت؛ dateTime شمسی و timestamp همان زمان به Unix time میلی‌ثانیه.
payments[].lockedردیف نزد سازمان است (یا ممکن است باشد) و دیگر تغییر نمی‌کند.
payments[].registration.statusوضعیت ثبت در کارپوشه؛ مقادیر در جدول زیر.
payments[].registration.code، messageکد و پیام رسمی سازمان در رد شدن (مثلاً 10014)، یا کد داخلی خطای انتقال.
payments[].registration.registeredAmountمبلغی که برای این ردیف به کارپوشه فرستاده شده (paidAmount، بدون سهم مالیات بر ارزش افزوده)؛ فقط وقتی ثبتی وجود دارد.
payments[].registration.registeredAt، lastAttemptAt، taxCreateDateزمان ثبت موفق، زمان آخرین تلاش و createDate برگشتی از سازمان (Unix time میلی‌ثانیه).

مقادیر status

statusمعنااقدام
registeredسازمان ثبت پرداخت را با SUCCESS پذیرفته است.—
queuedدر صف ثبت یا تلاش مجدد خودکار (پاسخ سازمان به موقع نرسیده یا خطای گذرا داشته).بعداً با inquiryPayment پیگیری کنید.
rejectedسازمان با خطای رسمی رد کرده است (code و message).علت را رفع کنید؛ ردیف ردشده در پنل قابل ویرایش است.
unknownدرخواست به سازمان رسیده ولی پاسخ آن گم شده است؛ ممکن است ثبت شده باشد.خودکار تکرار نمی‌شود؛ ابتدا کارپوشه را بررسی کنید و در صورت نیاز از پنل «تلاش مجدد» بزنید.
notApplicableهرگز فرستاده نشده است (تسویه نقدی، صورتحساب ابطالی، روش یا تاریخ نامعتبر).—
failedتلاش‌های خودکار به پایان رسیده است.از پنل «تلاش مجدد» بزنید یا با پشتیبانی تماس بگیرید.
sentWithInvoiceردیف همراه خود صورتحساب به سازمان رفته است (بخش Payments صورتحساب).—
notRegisteredصورتحساب هنوز ارسال نشده و ردیف با خود صورتحساب خواهد رفت.—
registerPayment response
{
  "result": 1,
  "traceId": "0f6c5f7a-2f6b-4a4f-9f5a-1b2c3d4e5f60",
  "message": "پرداخت‌ها ثبت شدند.",
  "data": [
    {
      "internalId": "INV-10001",
      "taxId": "A111H104EA6001D0B32AC6",
      "status": 3,
      "statusInquery": "AWAITING_REACTION",
      "paymentRegistration": { "eligible": true, "rowsLocked": true },
      "payments": [
        {
          "id": 50622,
          "type": 6,
          "dateTime": "1405/06/25 10:30",
          "timestamp": 1789446000000,
          "price": 25000000,
          "traceNo": "13023878656431",
          "terminalNumber": "1302387",
          "description": "قسط دوم",
          "locked": true,
          "registration": {
            "status": "registered",
            "registeredAt": 1789446065000,
            "taxCreateDate": 1789446064000,
            "registrationId": 12,
            "registeredAmount": 22727273
          }
        }
      ]
    }
  ]
}
این سرویس فقط پرداخت‌های بعد از ارسال را ثبت می‌کند. پرداخت‌هایی که هنگام ثبت صورتحساب در payments[] (مدل ساده) یا Payments (JSON سازمان) می‌فرستید همراه خود صورتحساب به سازمان می‌روند و در استعلام با وضعیت sentWithInvoice دیده می‌شوند.
Tax Workspace / Bulk / Excel

کارپوشه مالیاتی، واکنش گروهی و فایل Excel

این API همان منطق مرکزی کارپوشه در پنل را اجرا می‌کند. کسب‌وکار، کد اقتصادی، Profile و Delegation فقط از Access Token و Context احرازشده سرور تعیین می‌شوند؛ هیچ‌یک از مقادیر داخل JSON یا Excel نمی‌توانند Tenant یا مؤدی را انتخاب یا تغییر دهند.

احراز هویت و Internal Permission: همه مسیرها از client_credentials نوع Customer استفاده می‌کنند. عملیات صرفاً خواندنی Export، Template و Result به scope v2.invoice.read نیاز دارند. Preview هیچ PUT سازمانی اجرا نمی‌کند، اما چون Actionability و Permission هر ردیف را با همان Preflight عملیات بررسی می‌کند، مانند Bulk/Execute به scope v2.invoice.send نیاز دارد. نبود scope با HTTP 403 برگردانده می‌شود. API Key قدیمی فقط تا مهلت مهاجرت اعلام‌شده و با همان هدرهای Deprecation قبلی پشتیبانی می‌شود.
Delegation Permission: مشاهده قرارداد به contract.brokerage:view وابسته است. تأیید یا رد قرارداد علاوه بر scope داخلی، به Permission واگذارشده contract.brokerage:manage و روشن بودن TaxWorkspace.ContractReactionsEnabled نیاز دارد. Tokenهای قبلی فاقد این Permission برای قابلیت‌های دیگر Invalid نمی‌شوند.
فعال‌سازی Host: این Host با TaxWorkspace.Enabled=false تحویل می‌شود. پیش از فعال‌سازی Production باید Environment، URLهای رسمی، Client ID، مسیرهای Certificate/Private Key و Encryption Secret از Environment/Secret Store همان App Pool تنظیم و دسترسی خواندن کنترل شود؛ مقدار محرمانه نباید در Web.config قرار گیرد. چون API v2 همان Profile، Delegation و داده رمز‌شده MegaPayBackend را استفاده می‌کند، مقدار TRUSTED_COMPANY_SERVICES_ENCRYPTION_KEY، Environment و Trusted Company Client ID باید دقیقاً با Host اصلی MegaPayBackend یکسان باشند و endpoint/certificate identity نیز همسان تنظیم شود؛ برای این Host کلید جداگانه تولید نکنید.

Endpointها

MethodEndpointScopeکاربرد
PUT/api/v2/tax-workspace/invoices/bulkv2.invoice.sendApprove/Reject گروهی صورتحساب خرید، خرید مجازی و فروش مجازی با JSON.
PUT/api/v2/tax-workspace/brokerage-contracts/bulkv2.invoice.sendApprove/Reject گروهی قرارداد شخص ثالث؛ هر قرارداد با Request مستقل سازمان اجرا می‌شود.
GET/api/v2/tax-workspace/invoices/{kind}/excel/exportv2.invoice.readخروجی همه نتایج منطبق با Filter و Sort، نه فقط صفحه جاری.
GET/api/v2/tax-workspace/invoices/{kind}/excel/templatev2.invoice.readنمونه امن .xlsx برای بخش‌های دارای واکنش.
POST/api/v2/tax-workspace/invoices/{kind}/excel/previewv2.invoice.sendآپلود multipart، Parse و Preflight خواندنی Actionability/Permission؛ هیچ PUT سازمانی اجرا نمی‌شود.
POST/api/v2/tax-workspace/invoices/{kind}/excel/executev2.invoice.sendاجرای Preview تأییدشده با previewToken کوتاه‌عمر.
GET/api/v2/tax-workspace/brokerage-contracts/excel/exportv2.invoice.readخروجی Filterشده قراردادها.
GET/api/v2/tax-workspace/brokerage-contracts/excel/templatev2.invoice.readنمونه عملیات قرارداد.
POST/api/v2/tax-workspace/brokerage-contracts/excel/previewv2.invoice.sendاعتبارسنجی و Preflight قرارداد بدون اجرای PUT.
POST/api/v2/tax-workspace/brokerage-contracts/excel/executev2.invoice.sendاجرای عملیات قرارداد پس از Confirmation.
POST/api/v2/tax-workspace/excel/resultv2.invoice.readدریافت فایل نتیجه با resultToken متعلق به همان Company/API Client.

Kind و Filter خروجی صورتحساب

مقادیر مجاز {kind} عبارت‌اند از purchase، sales، virtual-purchase و virtual-sales. فروش عادی فقط Export دارد و endpoint واکنش ساختگی برای آن ایجاد نشده است. خرید مجازی از همان سرویس رسمی واکنش خرید استفاده می‌کند.

Filterتوضیح
periodدوره مالیاتی با قالب مورد قبول سرویس سازمان.
status و patternوضعیت و الگوی صورتحساب.
issuanceDateFrom / issuanceDateToبازه تاریخ صدور با epoch milliseconds.
contractBrokerageNumberشماره قرارداد شخص ثالث مرتبط.
sellerRoleفقط در فهرست فروش کارپوشه.

Export قرارداد نیز Filterهای contractBrokerageId، contractBrokerageNumber، contractBrokerageInternalNumber، status، کدهای اقتصادی طرفین و بازه‌های issue/create date را می‌پذیرد. سرور صفحات حداکثر ۱۰تایی سازمان را کنترل‌شده واکشی می‌کند و از سقف TaxWorkspace.ExcelExportMaxRows عبور نمی‌کند.

JSON Bulk صورتحساب

در حالت ساده فقط kind، آرایه taxIds و action ارسال می‌شود. Backend روز صدور و دوره مالیاتی را از بخش روز رسمی TaxId استخراج می‌کند، حداکثر دو تاریخ محتمل تهران را در فهرست واگذارشده همان کارپوشه استعلام می‌گیرد و فقط TaxId دقیق با وضعیت AWAITING_REACTION را قبل از PUT می‌پذیرد. بنابراین Client برای تأیید نیازی به ارسال period یا تاریخ ندارد و TaxId نیز نمی‌تواند کارپوشه دیگری را انتخاب کند.

Minimal invoice approve
{
  "kind": "purchase",
  "taxIds": ["<TaxId1>", "<TaxId2>"],
  "action": "APPROVE"
}
Minimal invoice reject
{
  "kind": "purchase",
  "taxIds": ["<TaxId1>", "<TaxId2>"],
  "action": "REJECT",
  "rejectReasons": ["PRODUCT", "AMOUNT"],
  "rejectDescription": "مغایرت اطلاعات ردیف‌ها"
}

Action فقط APPROVE یا REJECT است. دلیل‌های مجاز: NOT_INFORMED، BUYER، SELLER، PRODUCT، AMOUNT، PAYMENT و OTHER. برای OTHER توضیح الزامی است. برای Actionهای متفاوت در یک درخواست، شکل تفصیلی سازگار با نسخه قبل یعنی operations[] همچنان پشتیبانی می‌شود؛ در این شکل period و issuanceDate اختیاری‌اند و در صورت حذف از TaxId استخراج می‌شوند. ارسال هم‌زمان taxIds و operations با invalid_bulk_shape رد می‌شود.

Mixed invoice bulk request
{
  "kind": "purchase",
  "operations": [
    {
      "taxId": "A111111111111111111111",
      "period": "140501",
      "issuanceDate": 1780000000000,
      "action": "APPROVE",
      "rejectReasons": [],
      "rejectDescription": null
    },
    {
      "taxId": "A222222222222222222222",
      "period": "140501",
      "issuanceDate": 1780000000000,
      "action": "REJECT",
      "rejectReasons": ["PRODUCT", "AMOUNT"],
      "rejectDescription": "مغایرت ردیف نمونه"
    }
  ]
}
Bulk response schema
{
  "status": "OK",
  "data": {
    "total": 2,
    "success": 1,
    "failed": 1,
    "skipped": 0,
    "results": [
      {
        "identifier": "A111111111111111111111",
        "period": "140501",
        "issuanceDate": 1780000000000,
        "action": "APPROVE",
        "status": "SUCCESS",
        "code": null,
        "message": "عملیات انجام شد.",
        "correlationId": "00000000-0000-0000-0000-000000000001"
      },
      {
        "identifier": "A222222222222222222222",
        "period": "140501",
        "issuanceDate": 1780000000000,
        "action": "REJECT",
        "status": "FAILED",
        "code": "invoice_not_actionable",
        "message": "صورتحساب در وضعیت قابل واکنش نیست.",
        "correlationId": "00000000-0000-0000-0000-000000000001"
      }
    ]
  },
  "errors": [],
  "fieldErrors": [],
  "correlationId": "00000000-0000-0000-0000-000000000001"
}

JSON Bulk قرارداد

Contract numbers with one action
{
  "contractBrokerageNumbers": ["000000000001", "000000000002"],
  "action": "APPROVE"
}
All actionable contracts in current workspace
{
  "allForCurrentEconomicNumber": true,
  "economicNumber": "<CurrentWorkspaceEconomicNumber>",
  "action": "APPROVE"
}

برای انتخاب همه، فقط قراردادهای AWAITING_REACTION و قابل اقدام در کارپوشه احرازشده فعلی انتخاب می‌شوند. economicNumber اختیاری و صرفاً تأیید تطابق است؛ Backend هرگز از آن برای انتخاب Tenant یا Delegation استفاده نمی‌کند و مقدار متفاوت را با economic_number_context_mismatch رد می‌کند. شکل قبلی operations[] با contractBrokerageId یا contractBrokerageNumber همچنان برای Actionهای ترکیبی معتبر است، ولی سه روش انتخاب را نمی‌توان هم‌زمان فرستاد. endpoint رسمی قرارداد یک شناسه می‌پذیرد؛ بنابراین API هر Contract را با Request مستقل و concurrency محدود اجرا می‌کند و batch ساختگی برای سازمان نمی‌سازد. تعداد Operationهای واقعاً قابل اجرا در هر درخواست JSON یا فایل Excel با TaxWorkspace.ContractBulkMaxActions محدود می‌شود؛ مقدار پیش‌فرض امن 20 و بازه قابل تنظیم 1..100 است. ردیف‌های بدون Action یا نامعتبر در این سقف اجرایی شمرده نمی‌شوند.

Chunking، Atomicity و Partial Success

تعداد Operationهای واقعاً قابل اجرای صورتحساب در یک درخواست JSON یا فایل Excel با TaxWorkspace.InvoiceBulkMaxActions محدود می‌شود؛ مقدار پیش‌فرض 20 و بازه قابل تنظیم 1..100 است. این سقف با chunk حداکثر ۱۰تایی هر Request سازمان تفاوت دارد.

  • Approve صورتحساب‌ها در chunkهای حداکثر ۱۰تایی ارسال می‌شود.
  • Rejectها ابتدا بر اساس ترکیب دقیق Reasons و Description گروه‌بندی و سپس حداکثر ۱۰تایی می‌شوند.
  • Atomicity در سطح هر Request سازمان حفظ می‌شود؛ Fail شدن یک chunk همه اعضای همان chunk را Fail گزارش می‌کند.
  • فایل بزرگ می‌تواند Partial Success داشته باشد؛ نتیجه هر ردیف در response و فایل نتیجه مشخص است.
  • برای PUT هیچ Retry کور انجام نمی‌شود. نتیجه مبهم با GET Recheck فعلی بررسی و در صورت عدم قطعیت با AMBIGUOUS گزارش می‌شود.

این endpointها به‌صورت synchronous و با سقف اجرایی مشخص کار می‌کنند. Host فقط برای مسیر /api/v2/tax-workspace دارای executionTimeout=7200 ثانیه است تا عملیات ترتیبی قرارداد قبل از تکمیل توسط timeout پیش‌فرض ASP.NET قطع نشود. Client باید timeout متناسب با تعداد عملیات تنظیم کند؛ در صورت timeout یا پاسخ مبهم، PUT را کورکورانه تکرار نکند و ابتدا وضعیت‌ها و نتیجه ثبت‌شده را بررسی کند.

Excel: Export، Preview، Confirmation و Result

  1. فایل واقعی Filterشده یا Template را دانلود کنید.
  2. نمونه صورتحساب فقط چهار ستون فارسی «شناسه یکتای مالیاتی»، «عملیات»، «دلایل رد» و «توضیحات رد» دارد. نمونه قرارداد فقط «شماره قرارداد» و «عملیات» دارد. چند دلیل فارسی با ; جدا می‌شوند؛ مانند «کالا یا خدمت;مبلغ صورتحساب».
  3. برای واکنش به همه قراردادهای قابل اقدام همین کارپوشه، در ستون «شماره قرارداد» مقدار «همه» را بنویسید. فایل هیچ کد اقتصادی یا شناسه Tenant دریافت نمی‌کند.
  4. فایل را با multipart/form-data و نام فیلد file برای Preview ارسال کنید. Preview هیچ واکنشی اجرا نمی‌کند.
  5. Summary شامل قابل اجرا، Approve، Reject، بدون Action و خطادار را بررسی کنید.
  6. previewToken کوتاه‌عمر را با endpoint Execute ارسال کنید.
  7. پس از اجرا، فایل نتیجه را با resultToken دریافت کنید.

Workbook جدید به‌صورت راست‌به‌چپ و با Sheetهای فارسی داده‌ها، راهنما و فهرست‌ها تولید می‌شود. عنوان ستون‌ها، وضعیت‌ها، نوع‌ها، نقش‌ها، عملیات «تأیید/رد» و دلیل‌های رد با اصطلاحات فارسی مستندات نمایش داده می‌شوند و Backend آن‌ها را پیش از اجرا به کدهای فنی ثابت تبدیل می‌کند. فایل‌های قدیمی دارای Sheet Data و عنوان‌ها یا کدهای انگلیسی همچنان قابل بارگذاری‌اند. فایل نتیجه ستون‌های فارسی «نتیجه عملیات»، «زمان پردازش»، «کد پاسخ»، «پیام پاسخ» و «شناسه رهگیری» را اضافه می‌کند. هیچ Token، Secret یا Authorization Header در فایل نوشته نمی‌شود. Claim اجرای Excel با تنظیم TaxWorkspace.ExcelExecutionRetentionHours در بازه 3..168 ساعت نگه‌داری می‌شود و مقدار پیش‌فرض آن 24 ساعت است.

Excel Preview response
{
  "status": "OK",
  "data": {
    "previewToken": "<PreviewToken>",
    "batchCorrelationId": "00000000-0000-0000-0000-000000000001",
    "expiresAt": 1780000900000,
    "summary": {
      "total": 2,
      "executable": 1,
      "approve": 1,
      "reject": 0,
      "noAction": 0,
      "invalid": 1
    },
    "rows": [
      {
        "rowNumber": 2,
        "identifier": "<SampleTaxId1>",
        "period": "140501",
        "issuanceDate": 1780000000000,
        "action": "APPROVE",
        "rejectReasons": [],
        "rejectDescription": null,
        "errors": [],
        "warnings": [],
        "previewStatus": "EXECUTABLE",
        "previewCode": null,
        "previewMessage": null
      },
      {
        "rowNumber": 3,
        "identifier": "<SampleTaxId2>",
        "action": "REJECT",
        "errors": ["برای رد صورتحساب حداقل یک دلیل لازم است."],
        "warnings": [],
        "previewStatus": "VALIDATION_ERROR",
        "previewCode": "invalid_excel_row",
        "previewMessage": "برای رد صورتحساب حداقل یک دلیل لازم است."
      }
    ]
  },
  "errors": [],
  "fieldErrors": [],
  "correlationId": "00000000-0000-0000-0000-000000000001"
}
Excel Execute response
{
  "status": "OK",
  "data": {
    "batchCorrelationId": "00000000-0000-0000-0000-000000000001",
    "resultToken": "<ResultToken>",
    "summary": {
      "total": 2,
      "success": 1,
      "failed": 1,
      "skipped": 0,
      "batchCorrelationId": "00000000-0000-0000-0000-000000000001",
      "results": [
        {
          "identifier": "<SampleTaxId1>",
          "period": "140501",
          "issuanceDate": 1780000000000,
          "action": "APPROVE",
          "status": "SUCCESS",
          "code": null,
          "message": "عملیات انجام شد.",
          "correlationId": "00000000-0000-0000-0000-000000000001"
        },
        {
          "identifier": "<SampleTaxId2>",
          "action": "REJECT",
          "status": "VALIDATION_ERROR",
          "code": "invalid_excel_row",
          "message": "برای رد صورتحساب حداقل یک دلیل لازم است.",
          "correlationId": "00000000-0000-0000-0000-000000000001"
        }
      ]
    }
  },
  "errors": [],
  "fieldErrors": [],
  "correlationId": "00000000-0000-0000-0000-000000000001"
}
فقط فایل واقعی .xlsx با Content-Type رسمی application/vnd.openxmlformats-officedocument.spreadsheetml.sheet یا fallback رایج application/octet-stream پذیرفته می‌شود. در هر دو حالت پسوند، ZIP/OpenXML magic و ساختار package در Backend کنترل می‌شود. هدر Content-Length برای multipart الزامی است و Upload chunked بدون آن با HTTP 411 رد می‌شود. فایل Macro، رمزدار، دارای پسوند یا Magic نامعتبر، Formula قابل ارزیابی، Duplicate ID یا بیش از سقف Size/Rows رد می‌شود. مقدار پیش‌فرض Upload پنج MiB و حداکثر قابل تنظیم ۵۰ MiB است؛ سقف IIS فقط برای مسیر Tax Workspace روی ۵۱ MiB تنظیم شده تا overhead multipart را پوشش دهد. حداکثر ردیف پیش‌فرض ۵۰۰، حداکثر عملیات قابل اجرای صورتحساب و قرارداد در هر نوبت به‌صورت پیش‌فرض ۲۰ و عمر Token عملیات ۱۵ دقیقه است. Claim اجرای Excel برای replay ایمن و دریافت Result به‌صورت پیش‌فرض ۲۴ ساعت نگه‌داری می‌شود؛ مقدار نهایی می‌تواند در Production محدودتر باشد.

نمونه cURL

JSON bulk approve
curl -X PUT "https://api-v2.asatsp.ir/api/v2/tax-workspace/invoices/bulk" \
  -H "Authorization: Bearer <AccessToken with v2.invoice.send>" \
  -H "X-Correlation-ID: 00000000-0000-0000-0000-000000000001" \
  -H "Content-Type: application/json; charset=utf-8" \
  -d '{"kind":"purchase","taxIds":["<TaxId1>","<TaxId2>"],"action":"APPROVE"}'
Excel preview then execute
curl -X POST "https://api-v2.asatsp.ir/api/v2/tax-workspace/invoices/purchase/excel/preview" \
  -H "Authorization: Bearer <AccessToken with v2.invoice.send>" \
  -F "file=@operations.xlsx;type=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"

curl -X POST "https://api-v2.asatsp.ir/api/v2/tax-workspace/invoices/purchase/excel/execute" \
  -H "Authorization: Bearer <AccessToken with v2.invoice.send>" \
  -H "Content-Type: application/json" \
  -d '{"previewToken":"<PreviewToken>"}'

curl -X POST "https://api-v2.asatsp.ir/api/v2/tax-workspace/excel/result" \
  -H "Authorization: Bearer <AccessToken with v2.invoice.read>" \
  -H "Content-Type: application/json" \
  -d '{"resultToken":"<ResultToken>"}' \
  --output tax-workspace-result.xlsx

Status و Error Codeها

HTTPCode نمونهمعنا
200invalid_excel_row / invoice_not_awaiting_reaction / contract_not_actionable / tax_workspace_contract_reactions_disabled / tax_workspace_contract_permission_missingخطای Validation، Actionability، Feature یا Delegation Permission در مسیرهای گروهی خطای HTTP کل Batch نیست: Preview آن را در data.rows[].previewCode و previewStatus، JSON Bulk در data.results[] و Execute در data.summary.results[] برمی‌گرداند.
400invalid_bulk_size / invalid_excel_preview_token / tax_workspace_excel_file_requiredاندازه Batch، Token پیش‌نمایش یا ساختار multipart نامعتبر است.
401unauthorizedAccess Token/API Key ارسال نشده یا معتبر نیست.
403forbidden / tax_workspace_excel_token_context_mismatchScope داخلی لازم وجود ندارد، یا Token متعلق به Company، API Client، کارپوشه یا نوع عملیات جاری نیست.
408request_cancelledدرخواست در زمان پردازش لغو شده یا زمان آن به پایان رسیده است.
409tax_workspace_excel_export_row_limit_exceeded / tax_workspace_invoice_bulk_action_limit_exceeded / tax_workspace_contract_bulk_action_limit_exceeded / tax_workspace_excel_execution_in_progress / tax_workspace_excel_execution_outcome_unknown / tax_workspace_excel_token_expired / tax_workspace_excel_result_expiredسقف نتیجه یا عملیات رد شده، اجرای همان Preview در حال انجام یا نتیجه قبلی نامشخص است، یا مهلت Preview/Result پایان یافته است. این پاسخ‌ها مجوز Retry کور PUT نیستند.
411tax_workspace_excel_content_length_requiredارسال Content-Length برای Upload الزامی است.
413tax_workspace_excel_file_too_large / tax_workspace_excel_row_limit_exceededحجم فایل یا تعداد ردیف‌های Upload از سقف تنظیم‌شده بیشتر است.
415tax_workspace_excel_invalid_content_typeفایل multipart، پسوند یا Content-Type معتبر نیست.
503service_not_configured / organization_unavailableHost امن تنظیم نشده یا سرویس سازمان موقتاً در دسترس نیست.

همه پاسخ‌های JSON از Envelope شامل status، data، errors[]، fieldErrors[] و correlationId استفاده می‌کنند. همان Correlation در هدر X-Correlation-ID نیز بازگردانده می‌شود. فایل‌های موفق با HTTP 200 و Content-Type رسمی xlsx برمی‌گردند؛ خطای تولید یا دسترسی فایل همیشه JSON است.

خاموش بودن TaxWorkspace.ContractReactionsEnabled فقط اجرای Approve/Reject قرارداد را متوقف می‌کند؛ مشاهده فهرست و جزئیات، Filter، Pagination، Export، Template، صورتحساب‌ها و Delegation موجود همچنان فعال می‌مانند. Preview فایل همچنان Parse و خوانده می‌شود، اما ردیف‌های دارای Action با وضعیت Feature disabled غیرقابل اجرا گزارش می‌شوند. اگر Flag روشن ولی Permission واگذارشده موجود نباشد، پاسخ باید حالت Permission missing را از Feature disabled متمایز کند.
Checklist

چک‌لیست پیش از ثبت یا ارسال

کنترلنتیجه مورد انتظار
هدر احراز هویتAuthorization: Bearer <AccessToken> ارسال شده باشد. در دوره مهاجرت، Customer API Key legacy هم تا تاریخ sunset پذیرفته می‌شود.
شناسه فروشندهHeader.tins با کد اقتصادی شرکت صاحب کلید برابر باشد.
یکتایی درخواستبرای هر درخواست جدید یک uuid تازه تولید شده باشد و همه internalIdها برای شرکت احراز هویت‌شده یکتا باشند.
ساختار صورتحسابهر عضو data[] دارای Header و Body غیرخالی باشد.
ماده ۹برای صورتحساب خارج از مهلت ۱۵ روز، Header.insr=1 و Header.indati2m معتبر را هم‌زمان ارسال کنید. صورتحساب عادی و کلاینت قدیمی به این دو فیلد نیاز ندارند.
جمع ردیف‌هاadis، tsstam و جمع‌های Header دقیقاً با Body برابر باشند.
تسویه و پرداختcap + insp <= tbill و مجموع پرداخت‌ها از tbill بیشتر نباشد.
الگوهای خاصفیلدهای اختصاصی ارز، طلا، پیمانکاری، قبوض، پرواز، صادرات، بارنامه، فرآورده‌های نفتی، اوراق بهادار مبتنی بر کالا، بیمه و فروش زنجیره تکمیل شده باشند.
الگوی ۱۱ (بورس کالا)adis (در مدل ساده amountAfterDiscount) دقیقاً برابر ارزش معامله در اعلامیه فروش بورس و Header.tadis برابر آن باشد؛ fee/unitPrice، prdis و dis ارسال نشده باشند. جزئیات در مبلغ واحد در الگوی ۱۱.
نگه‌داری شناسه‌هاuuid، internalId، traceId و در پاسخ موفق asatspId را ذخیره کنید.