{
  "info": {
    "_postman_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "PCE AP Write",
    "description": "NetSuite RESTlet: PCE AP Write (`customscript_pce_rl_ap_write` / `customdeploy_pce_rl_ap_write`).\n\n## Auth (OAuth 2.0)\nThis collection uses **Bearer access tokens** (OAuth 2.0). OAuth 1.0a / TBA is not used.\n\n### Recommended for vendors (M2M)\n1. Create Integration with **Client Credentials (Machine to Machine)** + scope **RESTlets**.\n2. Upload certificate under Setup → Integration → OAuth 2.0 Client Credentials (M2M) Setup.\n3. Generate a JWT client assertion (see `docs/postman/generate-client-assertion.js`).\n4. Run **Auth → POST Get Access Token (Client Credentials)**.\n5. `accessToken` is saved automatically; other requests use `Authorization: Bearer {{accessToken}}`.\n\n### Alternative for interactive testing\nUse Authorization Code Grant in Postman (Authorize using browser), then paste the access token into `accessToken`.\n\nToken URL:\n`https://{{accountId}}.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token`\n\n## Docs\n- ZH: `docs/PCE_GUI_MODULE_ap-write-api-integration-guide.md`\n- EN: `docs/PCE_GUI_MODULE_ap-write-api-integration-guide.en.md`",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{accessToken}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "accountId",
      "value": "TD1234567"
    },
    {
      "key": "tokenUrl",
      "value": "https://{{accountId}}.suitetalk.api.netsuite.com/services/rest/auth/oauth2/v1/token"
    },
    {
      "key": "restletUrl",
      "value": "https://{{accountId}}.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script=CUSTOM_SCRIPT_ID&deploy=CUSTOM_DEPLOY_ID"
    },
    {
      "key": "clientId",
      "value": ""
    },
    {
      "key": "clientAssertion",
      "value": ""
    },
    {
      "key": "accessToken",
      "value": ""
    },
    {
      "key": "sampleVendorId",
      "value": "123"
    },
    {
      "key": "sampleSubsidiaryId",
      "value": "1"
    },
    {
      "key": "sampleApAccountId",
      "value": "220"
    },
    {
      "key": "sampleExpenseAccountId",
      "value": "610"
    },
    {
      "key": "sampleItemId",
      "value": "10"
    },
    {
      "key": "sampleTransactionId",
      "value": "789"
    },
    {
      "key": "sampleInvoiceNo",
      "value": "CA12345678"
    },
    {
      "key": "sampleInvoiceNo2",
      "value": "CA12345679"
    },
    {
      "key": "sampleBuyerTaxId",
      "value": "24536806"
    },
    {
      "key": "sampleSellerTaxId",
      "value": "24536806"
    },
    {
      "key": "sampleDocPeriod",
      "value": "11508"
    },
    {
      "key": "sampleFilingPeriod",
      "value": "11508"
    },
    {
      "key": "sampleDocDate",
      "value": "2026/08/01"
    }
  ],
  "item": [
    {
      "name": "0. Auth (OAuth 2.0)",
      "item": [
        {
          "name": "POST Get Access Token (Client Credentials)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const json = pm.response.json();",
                  "if (json.access_token) {",
                  "    pm.collectionVariables.set('accessToken', json.access_token);",
                  "    console.log('accessToken saved; expires_in=', json.expires_in);",
                  "} else {",
                  "    console.warn('No access_token in response', json);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/x-www-form-urlencoded"
              }
            ],
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                {
                  "key": "grant_type",
                  "value": "client_credentials",
                  "type": "text"
                },
                {
                  "key": "client_assertion_type",
                  "value": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
                  "type": "text"
                },
                {
                  "key": "client_assertion",
                  "value": "{{clientAssertion}}",
                  "type": "text"
                }
              ]
            },
            "url": "{{tokenUrl}}",
            "description": "OAuth 2.0 Client Credentials (M2M).\n\n1. Generate a signed JWT with `docs/postman/generate-client-assertion.js` (needs clientId, certificateId/kid, private key PEM).\n2. Paste JWT into collection variable `clientAssertion`.\n3. Send this request.\n4. `accessToken` is written to the collection for subsequent RESTlet calls.\n\nJWT claims typically:\n- `iss` = Client ID\n- `scope` = `restlets` (add `rest_webservices` only if needed)\n- `aud` = token URL\n- `iat` / `exp` (short-lived, e.g. 1 hour max for assertion)"
          },
          "response": []
        }
      ]
    },
    {
      "name": "1. Method check",
      "item": [
        {
          "name": "GET — Not allowed (expect PCE_METHOD_NOT_ALLOWED)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": "{{restletUrl}}",
            "description": "GET is not supported. Expect success=false and error.code=PCE_METHOD_NOT_ALLOWED. Use POST for all operations."
          },
          "response": []
        }
      ]
    },
    {
      "name": "2. Validate only",
      "item": [
        {
          "name": "POST — apInvoiceOnly (validateOnly)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"ae8d5d82-fc15-4eac-a82b-36603ef41a7d\",\n  \"action\": \"apInvoiceOnly\",\n  \"options\": {\n    \"validateOnly\": true\n  },\n  \"apInvoices\": [\n    {\n      \"docFormatCode\": \"21\",\n      \"invCatCode\": \"01\",\n      \"docDate\": \"{{sampleDocDate}}\",\n      \"docPeriod\": \"{{sampleDocPeriod}}\",\n      \"filingPeriod\": \"{{sampleFilingPeriod}}\",\n      \"deductionCode\": \"1\",\n      \"invoiceNo\": \"{{sampleInvoiceNo}}\",\n      \"buyerTaxId\": \"{{sampleBuyerTaxId}}\",\n      \"sellerTaxId\": \"{{sampleSellerTaxId}}\",\n      \"taxType\": \"1\",\n      \"salesAmount\": 1000,\n      \"taxAmount\": 50,\n      \"totalAmount\": 1050,\n      \"fileBizTax\": true,\n      \"clientRef\": \"postman-inv-1\"\n    }\n  ]\n}"
            },
            "url": "{{restletUrl}}",
            "description": "Runs AP validation without creating records."
          },
          "response": []
        },
        {
          "name": "POST — apInvoiceOnly format 25 (validateOnly)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"b7e4c1a2-3d5f-4e6a-9b8c-1d2e3f4a5b6c\",\n  \"action\": \"apInvoiceOnly\",\n  \"options\": {\n    \"validateOnly\": true\n  },\n  \"apInvoices\": [\n    {\n      \"docFormatCode\": \"25\",\n      \"invCatCode\": \"06\",\n      \"docDate\": \"{{sampleDocDate}}\",\n      \"docPeriod\": \"{{sampleDocPeriod}}\",\n      \"filingPeriod\": \"{{sampleFilingPeriod}}\",\n      \"deductionCode\": \"1\",\n      \"invoiceNo\": \"{{sampleInvoiceNo2}}\",\n      \"buyerTaxId\": \"{{sampleBuyerTaxId}}\",\n      \"sellerTaxId\": \"{{sampleSellerTaxId}}\",\n      \"taxType\": \"1\",\n      \"salesAmount\": 1000,\n      \"taxAmount\": 50,\n      \"totalAmount\": 1050,\n      \"fileBizTax\": true,\n      \"clientRef\": \"postman-inv-25-06\"\n    }\n  ]\n}"
            },
            "url": "{{restletUrl}}",
            "description": "Format 25 is split into two lookup rows; send invCatCode 06 (三聯式收銀機) or 07 (一般稅額電子發票). Runs validation only."
          },
          "response": []
        },
        {
          "name": "POST — transactionWithApInvoices (validateOnly)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"aaa95857-bf74-4be1-ae04-14d494dc596d\",\n  \"action\": \"transactionWithApInvoices\",\n  \"options\": {\n    \"validateOnly\": true\n  },\n  \"transaction\": {\n    \"id\": \"{{sampleTransactionId}}\",\n    \"apInvoices\": [\n      {\n        \"docFormatCode\": \"21\",\n      \"invCatCode\": \"01\",\n        \"docDate\": \"{{sampleDocDate}}\",\n        \"docPeriod\": \"{{sampleDocPeriod}}\",\n        \"filingPeriod\": \"{{sampleFilingPeriod}}\",\n        \"deductionCode\": \"1\",\n        \"invoiceNo\": \"{{sampleInvoiceNo}}\",\n        \"buyerTaxId\": \"{{sampleBuyerTaxId}}\",\n        \"sellerTaxId\": \"{{sampleSellerTaxId}}\",\n        \"taxType\": \"1\",\n        \"salesAmount\": 1000,\n        \"taxAmount\": 50,\n        \"totalAmount\": 1050,\n        \"fileBizTax\": true,\n        \"clientRef\": \"postman-inv-1-1\"\n      }\n    ],\n    \"clientRef\": \"postman-tx-1\"\n  }\n}"
            },
            "url": "{{restletUrl}}",
            "description": "Validates invoices (optional existing transaction.id). Does not create transaction or invoices."
          },
          "response": []
        }
      ]
    },
    {
      "name": "3. Write — AP invoices",
      "item": [
        {
          "name": "POST — apInvoiceOnly",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"39c7ab75-8319-4428-b553-b19e81edb3b1\",\n  \"action\": \"apInvoiceOnly\",\n  \"apInvoices\": [\n    {\n      \"docFormatCode\": \"21\",\n      \"invCatCode\": \"01\",\n      \"docDate\": \"{{sampleDocDate}}\",\n      \"docPeriod\": \"{{sampleDocPeriod}}\",\n      \"filingPeriod\": \"{{sampleFilingPeriod}}\",\n      \"deductionCode\": \"1\",\n      \"invoiceNo\": \"{{sampleInvoiceNo}}\",\n      \"buyerTaxId\": \"{{sampleBuyerTaxId}}\",\n      \"sellerTaxId\": \"{{sampleSellerTaxId}}\",\n      \"taxType\": \"1\",\n      \"salesAmount\": 1000,\n      \"taxAmount\": 50,\n      \"totalAmount\": 1050,\n      \"fileBizTax\": true,\n      \"clientRef\": \"postman-inv-1\"\n    }\n  ]\n}"
            },
            "url": "{{restletUrl}}",
            "description": "Creates AP invoice record(s) only. Do not send a create-transaction payload."
          },
          "response": []
        }
      ]
    },
    {
      "name": "4. Write — Transaction",
      "item": [
        {
          "name": "POST — transactionOnly (create Vendor Bill)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"47d0cebd-fa29-4e3f-b19b-3e4b41299100\",\n  \"action\": \"transactionOnly\",\n  \"transaction\": {\n    \"type\": \"vendorbill\",\n    \"fields\": {\n      \"entity\": \"{{sampleVendorId}}\",\n      \"subsidiary\": \"{{sampleSubsidiaryId}}\",\n      \"account\": \"{{sampleApAccountId}}\",\n      \"memo\": \"AP Write Postman\"\n    },\n    \"expenseLines\": [\n      {\n        \"account\": \"{{sampleExpenseAccountId}}\",\n        \"amount\": 1050\n      }\n    ],\n    \"clientRef\": \"postman-tx-1\"\n  }\n}"
            },
            "url": "{{restletUrl}}",
            "description": "Creates a Vendor Bill. Adjust field IDs/values for the target account. Must not include apInvoices."
          },
          "response": []
        },
        {
          "name": "POST — transactionOnly (reference existing)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"b9088724-7a65-4215-94e7-674b8d4cf947\",\n  \"action\": \"transactionOnly\",\n  \"transaction\": {\n    \"id\": \"{{sampleTransactionId}}\",\n    \"clientRef\": \"postman-tx-1\"\n  }\n}"
            },
            "url": "{{restletUrl}}",
            "description": "Resolves an existing transaction by internal id (no create)."
          },
          "response": []
        }
      ]
    },
    {
      "name": "5. Write — Transaction + AP invoices",
      "item": [
        {
          "name": "POST — transactionWithApInvoices (create + 2 invoices)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"24ef762f-2d5c-4e76-8c0c-e4cbd23ec47e\",\n  \"action\": \"transactionWithApInvoices\",\n  \"transaction\": {\n    \"type\": \"vendorbill\",\n    \"fields\": {\n      \"entity\": \"{{sampleVendorId}}\",\n      \"subsidiary\": \"{{sampleSubsidiaryId}}\",\n      \"account\": \"{{sampleApAccountId}}\",\n      \"memo\": \"AP Write + invoices\"\n    },\n    \"expenseLines\": [\n      {\n        \"account\": \"{{sampleExpenseAccountId}}\",\n        \"amount\": 2100\n      }\n    ],\n    \"apInvoices\": [\n      {\n        \"docFormatCode\": \"21\",\n      \"invCatCode\": \"01\",\n        \"docDate\": \"{{sampleDocDate}}\",\n        \"docPeriod\": \"{{sampleDocPeriod}}\",\n        \"filingPeriod\": \"{{sampleFilingPeriod}}\",\n        \"deductionCode\": \"1\",\n        \"invoiceNo\": \"{{sampleInvoiceNo}}\",\n        \"buyerTaxId\": \"{{sampleBuyerTaxId}}\",\n        \"sellerTaxId\": \"{{sampleSellerTaxId}}\",\n        \"taxType\": \"1\",\n        \"salesAmount\": 1000,\n        \"taxAmount\": 50,\n        \"totalAmount\": 1050,\n        \"fileBizTax\": true,\n        \"clientRef\": \"postman-inv-1-1\"\n      },\n      {\n        \"docFormatCode\": \"21\",\n      \"invCatCode\": \"01\",\n        \"docDate\": \"2026/08/02\",\n        \"docPeriod\": \"{{sampleDocPeriod}}\",\n        \"filingPeriod\": \"{{sampleFilingPeriod}}\",\n        \"deductionCode\": \"1\",\n        \"invoiceNo\": \"{{sampleInvoiceNo2}}\",\n        \"buyerTaxId\": \"{{sampleBuyerTaxId}}\",\n        \"sellerTaxId\": \"{{sampleSellerTaxId}}\",\n        \"taxType\": \"1\",\n        \"salesAmount\": 1000,\n        \"taxAmount\": 50,\n        \"totalAmount\": 1050,\n        \"fileBizTax\": true,\n        \"clientRef\": \"postman-inv-1-2\"\n      }\n    ],\n    \"clientRef\": \"postman-tx-1\"\n  },\n  \"options\": {\n    \"validateOnly\": false\n  }\n}"
            },
            "url": "{{restletUrl}}",
            "description": "Creates Vendor Bill then two linked AP invoices (1:N)."
          },
          "response": []
        },
        {
          "name": "POST — transactionWithApInvoices (attach to existing)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"932a72dd-14b3-4f70-b8f5-49a80c4ea493\",\n  \"action\": \"transactionWithApInvoices\",\n  \"transaction\": {\n    \"id\": \"{{sampleTransactionId}}\",\n    \"apInvoices\": [\n      {\n        \"docFormatCode\": \"21\",\n      \"invCatCode\": \"01\",\n        \"docDate\": \"{{sampleDocDate}}\",\n        \"docPeriod\": \"{{sampleDocPeriod}}\",\n        \"filingPeriod\": \"{{sampleFilingPeriod}}\",\n        \"deductionCode\": \"1\",\n        \"invoiceNo\": \"{{sampleInvoiceNo2}}\",\n        \"buyerTaxId\": \"{{sampleBuyerTaxId}}\",\n        \"sellerTaxId\": \"{{sampleSellerTaxId}}\",\n        \"taxType\": \"1\",\n        \"salesAmount\": 1000,\n        \"taxAmount\": 50,\n        \"totalAmount\": 1050,\n        \"fileBizTax\": true,\n        \"clientRef\": \"postman-inv-1-1\"\n      }\n    ],\n    \"clientRef\": \"postman-tx-1\"\n  }\n}"
            },
            "url": "{{restletUrl}}",
            "description": "Attaches a new AP invoice to an existing transaction id."
          },
          "response": []
        }
      ]
    },
    {
      "name": "6. Negative cases",
      "item": [
        {
          "name": "POST — invalid action",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"1e3633ed-053b-42b5-a4f1-c488db454834\",\n  \"action\": \"unknownAction\",\n  \"apInvoices\": [{ \"invoiceNo\": \"X\" }]\n}"
            },
            "url": "{{restletUrl}}",
            "description": "Expect success=false, code INVALID_ACTION."
          },
          "response": []
        },
        {
          "name": "POST — apInvoiceOnly with create transaction (rejected)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"1bf1c298-212f-45f1-93ba-973fcf537b4e\",\n  \"action\": \"apInvoiceOnly\",\n  \"apInvoices\": [\n    {\n      \"docFormatCode\": \"21\",\n      \"invCatCode\": \"01\",\n      \"invoiceNo\": \"{{sampleInvoiceNo}}\",\n      \"buyerTaxId\": \"{{sampleBuyerTaxId}}\",\n      \"clientRef\": \"postman-inv-1\"\n    }\n  ],\n  \"transaction\": {\n    \"type\": \"vendorbill\",\n    \"fields\": {\n      \"entity\": \"{{sampleVendorId}}\"\n    }\n  }\n}"
            },
            "url": "{{restletUrl}}",
            "description": "Expect TRANSACTION_NOT_ALLOWED."
          },
          "response": []
        },
        {
          "name": "POST — apInvoiceOnly invalid buyer tax id",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"6acb9bf7-7a1b-485e-8fb6-3e704e606539\",\n  \"action\": \"apInvoiceOnly\",\n  \"options\": {\n    \"validateOnly\": true\n  },\n  \"apInvoices\": [\n    {\n      \"docFormatCode\": \"21\",\n      \"invCatCode\": \"01\",\n      \"docDate\": \"{{sampleDocDate}}\",\n      \"docPeriod\": \"{{sampleDocPeriod}}\",\n      \"filingPeriod\": \"{{sampleFilingPeriod}}\",\n      \"deductionCode\": \"1\",\n      \"invoiceNo\": \"{{sampleInvoiceNo}}\",\n      \"buyerTaxId\": \"12345678\",\n      \"sellerTaxId\": \"{{sampleSellerTaxId}}\",\n      \"taxType\": \"1\",\n      \"salesAmount\": 1000,\n      \"taxAmount\": 50,\n      \"totalAmount\": 1050,\n      \"fileBizTax\": true,\n      \"clientRef\": \"postman-inv-1\"\n    }\n  ]\n}"
            },
            "url": "{{restletUrl}}",
            "description": "Expect PCE_AP_INVOICE_VALIDATION_ERROR (invalid tax ID checksum)."
          },
          "response": []
        },
        {
          "name": "POST — transactionOnly with apInvoices (rejected)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"requestId\": \"a109495b-7311-4302-b946-09345cbf9ae1\",\n  \"action\": \"transactionOnly\",\n  \"transaction\": {\n    \"type\": \"vendorbill\",\n    \"fields\": {\n      \"entity\": \"{{sampleVendorId}}\"\n    },\n    \"clientRef\": \"postman-tx-1\"\n  },\n  \"apInvoices\": [\n    {\n      \"invoiceNo\": \"{{sampleInvoiceNo}}\"\n    }\n  ]\n}"
            },
            "url": "{{restletUrl}}",
            "description": "Expect AP_INVOICES_NOT_ALLOWED."
          },
          "response": []
        }
      ]
    }
  ]
}
