PayPal #Invoice: Create Draft

PayPal: Invoice, Create Draft
Creates a draft invoice on the payment platform PayPal. To move the invoice from a draft to payable state, the SEND action is required. You can also store the invoice ID, invoice URL and status in the workflow data.
Configs
  • A1: Select HTTP_Authz (BasicAuthn User:ClientID Passwd:SECRET) *
  • A2: Set Boolean Tax-Inclusive (true: includes, false: DEFAULT)#{EL}
  • A3: Set Currency-Code (“USD”) *#{EL}
  • A4: Set Invoice-Num and Due-Date (in 2 lines)#{EL}
  • A5: Set Invoice-Date#{EL}
  • B1: Set INVOICER’s Registered Email-Address *#{EL}
  • B2: Set INVOICER’s Bizname/Name1/Name2/AdditionalInfo (4 lines)#{EL}
  • B3: Set INVOICER’s LogoUrl/WebsiteUrl/TaxId (in 3 lines)#{EL}
  • C1: Set RECIPIENT’s Email-Address *#{EL}
  • C2: Set RECIPIENT’s Language-Code for email message (eg “en-US”)#{EL}
  • C3: Set RECIPIENT’s Bizname/Name1/Name2 (in 3 lines)#{EL}
  • D: Set ADDITIONAL_RECIPIENTS Cc-Addresses (up to 10 lns)#{EL}
  • E: Set Tsv String for Items (ItemName/Qty/UnitAmount…) *#{EL}
  • F: Set Note to Recipient (Also displayed in notification)#{EL}
  • G: Set Terms and Conditions#{EL}
  • H: Set Memo (e.g. “BPMS pid: #{processInstanceId}”)#{EL}
  • I: Select STRING DATA for Paypal Invoice ID (update)
  • J: Select STRING DATA for invoicer_view_url (update)
  • K: Select STRING DATA for Invoice Status (update)
Script (click to open)
// Script for 'engine type: 3' ("GraalJS standard mode")
// PayPal Invoicing API v2 - Create a draft invoice (2026-11-06)

//////// START "main()" /////////////////////////////////////////////////////////////////
main();
function main(){ 

//// == Config Retrieving / 工程コンフィグの参照 ==
const oauth2   = configs.getObject( "AuthzConfA1" ); // required (Authz by Basic_AuthN)
  engine.log( " AutomatedTask Config: Authz Setting: " + oauth2.getName() );
const strTaxsetting     = configs.get( "StrConfA2" ) + "";
  let boolTaxinclusive  = false;
  if( strTaxsetting   === "true" ){
      boolTaxinclusive  = true;
  }
  engine.log( " AutomatedTask Config: Tax Inclusive: " + boolTaxinclusive );
const strCurrencycode   = configs.get( "StrConfA3" ) + ""; // required
  if( strCurrencycode === "" ){
    throw new Error( "\n AutomatedTask ConfigError:" +
                     " Config {A3 Currency Code} not specified \n" );
  }
const strInvnumInvdue   = configs.get( "StrConfA4" ) + "";
  let strInvnumber      = "";
  let strInvdue         = "";
  const arrInvnumInvdue = strInvnumInvdue.split("\n");
  if( arrInvnumInvdue.length >= 1 ){
    strInvnumber        = arrInvnumInvdue[0];
  }
  if( arrInvnumInvdue.length >= 2 ){
    strInvdue           = arrInvnumInvdue[1];
  }
  engine.log( " AutomatedTask Config: Invoice Number: " + strInvnumber );
  engine.log( " AutomatedTask Config: Invoice Due: " + strInvdue );
  const strInvdate        = configs.get( "StrConfA5" ) + "";
  engine.log( " AutomatedTask Config: Invoice Date: " + strInvdate );
const strInvoicerEmail  = configs.get( "StrConfB1" ) + ""; // required
const strInvoicerinfo   = configs.get( "StrConfB2" ) + "";
  let strInvoicerBizname = "";
  let strInvoicerName1   = "";
  let strInvoicerName2   = "";
  let strInvoicerAddit   = "";
  const arrInvoicerinfo = strInvoicerinfo.split("\n");
  if( arrInvoicerinfo.length >= 1 ){
    strInvoicerBizname  = arrInvoicerinfo[0];
  }
  if( arrInvoicerinfo.length >= 2 ){
    strInvoicerName1    = arrInvoicerinfo[1];
  }
  if( arrInvoicerinfo.length >= 3 ){
    strInvoicerName2    = arrInvoicerinfo[2];
  }
  if( arrInvoicerinfo.length >= 4 ){
    strInvoicerAddit    = arrInvoicerinfo[3];
  }
const strInvoicerProfile = configs.get( "StrConfB3" ) + "";
  let strInvoicerLogo   = "";
  let strInvoicerWeb    = "";
  let strInvoicerTaxid  = "";
  const arrInvoicerProfile = strInvoicerProfile.split("\n");
  if( arrInvoicerProfile.length >= 1 ){
    strInvoicerLogo     = arrInvoicerProfile[0];
  }
  if( arrInvoicerProfile.length >= 2 ){
    strInvoicerWeb      = arrInvoicerProfile[1];
  }
  if( arrInvoicerProfile.length >= 3 ){
    strInvoicerTaxid    = arrInvoicerProfile[2];
  }
const strRecipientEmail = configs.get( "StrConfC1" ) + ""; // required
  if( strRecipientEmail === "" ){
    throw new Error( "\n AutomatedTask ConfigError:" +
                     " Config {C1 Recipient Email} not specified \n" );
  }
  engine.log( " AutomatedTask Config: Recipient Email: " + strRecipientEmail );
const strRecipientLang  = configs.get( "StrConfC2" ) + "";
  engine.log( " AutomatedTask Config: Recipient Lang: " + strRecipientLang );
const strRecipientinfo  = configs.get( "StrConfC3" ) + "";
  let strRecipientBizname = "";
  let strRecipientName1   = "";
  let strRecipientName2   = "";
  const arrRecipientinfo  = strRecipientinfo.split("\n");
  if( arrRecipientinfo.length >= 1 ){
    strRecipientBizname = arrRecipientinfo[0];
  }
  if( arrRecipientinfo.length >= 2 ){
    strRecipientName1   = arrRecipientinfo[1];
  }
  if( arrRecipientinfo.length >= 3 ){
    strRecipientName2   = arrRecipientinfo[2];
  }
  engine.log( " AutomatedTask Config: Recipient BizName: " + strRecipientBizname );
  engine.log( " AutomatedTask Config: Recipient Name1: " + strRecipientName1 );
  engine.log( " AutomatedTask Config: Recipient Name2: " + strRecipientName2 );
  const strAdditionalRecipients = configs.get( "StrConfD" ) + "";
  const arrAdditionalRecipients = strAdditionalRecipients.split(/\r?\n/).map(line => line.trim()).filter(line => line !== "");
  if( strAdditionalRecipients === "" ){
    engine.log( " AutomatedTask Config: # of Additional Recipients: 0" );
  }else{
    engine.log( " AutomatedTask Config: # of Additional Recipients: " + arrAdditionalRecipients.length );
  }
const strTsvItems             = configs.get( "StrConfE" ) + "";
  if( strTsvItems   === "" ){
    throw new Error( "\n AutomatedTask ConfigError:" +
                     " Config {E TSV for Items} not specified \n" );
  }
  const arrTsvItems = strTsvItems.split(/\r?\n/).filter(line => line.trim() !== "");
  engine.log( " AutomatedTask Config: # of Items: " + arrTsvItems.length );
  if (arrTsvItems.length === 0) {throw new Error("商品明細がありません");}
const strNoteToRecipient      = configs.get( "StrConfF" ) + "";
const strTermsConditions      = configs.get( "StrConfG" ) + "";
const strMemo                 = configs.get( "StrConfH" ) + "";
const strPocketInvoiceid      = configs.getObject( "SelectConfI" );
const strPocketInvoiceurl     = configs.getObject( "SelectConfJ" );
const strPocketInvoiceStatus  = configs.getObject( "SelectConfK" );


//// == Data Retrieving / ワークフローデータの参照 ==
// (Nothing. Retrieved via Expression Language in Config Retrieving)


//// == Calculating / 演算 ==
// prepare request1: Get an Access Token
// (PayPal OAuth 2.0 credentials / Client Credentials)
// https://developer.paypal.com/docs/api/overview#get-credentials
// https://developer.paypal.com/docs/api/get-an-access-token-curl/
let postUri1 = "https://api-m.paypal.com/v1/oauth2/token";
let request1 = httpClient.begin(); // HttpRequestWrapper
    request1 = request1.authSetting( oauth2 ); // with "Authorization: Basic "
    request1 = request1.formParam( "grant_type", "client_credentials" );
engine.log( " AutomatedTask ApiRequest1 Prepared" );
// RFC 6749 - OAuth 2.0 - Client Credentials Grant
// - https://tools.ietf.org/html/rfc6749#section-4.4.2

// try request1
const response1 = request1.post( postUri1 ); // HttpResponseWrapper
engine.log( " AutomatedTask ApiRequest1 Start: " + postUri1 );
const response1Code = response1.getStatusCode() + ""; // (primitive string)
const response1Body = response1.getResponseAsString() + "";
engine.log( " AutomatedTask ApiResponse1 Status: " + response1Code );
if( response1Code !== "200"){
  throw new Error( "\n AutomatedTask UnexpectedResponseError: " +
                    response1Code + "\n" + response1Body + "\n" );
}

// parse response1
/*** engine.log( response1Body ); // debug
response sample
{
  "scope":"https://api.paypal.com/v1/payments/.※ https://uri.paypal.com/services/invoicing
           openid https://uri.paypal.com/payments/payouts ....",
  "access_token":"A21AAO9wpfXXXXXXXXXXL1P96HvZkRXXXXXXXXXXDlSVGSvUjlXXXXX...XXXXX",
  "token_type":"Bearer",
  "app_id":"APP-6BKXXX232XXX3090N",
  "expires_in":32400,
  "nonce":"2021-01-14T06:13:03Z1dgXXXCnnXXXgLKXXXgn0XXXcrJXXXoALXXXXBUXXXs"
}
***/
const response1Obj = JSON.parse( response1Body );
const strBearerToken = response1Obj.access_token;


// prepare request2: Create Invoice Draft in PayPal.com
let request2Obj = {};
    request2Obj.detail = {}; // "detail" object required in PayPal API
    if( strInvnumber !== "" ){
      request2Obj.detail.invoice_number = strInvnumber;
    }
    if( strInvdate !== "" ){
      request2Obj.detail.invoice_date = strInvdate;
    }
    request2Obj.detail.currency_code  = strCurrencycode; // required in PayPal API
    if( strNoteToRecipient !== "" ){
      request2Obj.detail.note         = strNoteToRecipient;
    }
    if( strTermsConditions !== "" ){
      request2Obj.detail.terms_and_conditions = strTermsConditions; /// API-Doc may be wrong
    }
    if( strMemo !== "" ){
      request2Obj.detail.memo           = strMemo;
    }
    if( strInvdue !== "" ){
      request2Obj.detail.payment_term = {
      term_type: "DUE_ON_DATE_SPECIFIED",
      due_date: strInvdue
      };
    }
    request2Obj.invoicer = {}; // required in this addon
    request2Obj.invoicer.email_address   = strInvoicerEmail;
    if( strInvoicerBizname !== "" ){
      request2Obj.invoicer.business_name = strInvoicerBizname;     /// No info in API-Doc
    }
    request2Obj.invoicer.name = {};
    request2Obj.invoicer.name.given_name = strInvoicerName1;
    request2Obj.invoicer.name.surname    = strInvoicerName2;
    if( strInvoicerLogo !== "" ){
      request2Obj.invoicer.logo_url = strInvoicerLogo;
    }
    if( strInvoicerWeb !== "" ){
      request2Obj.invoicer.website  = strInvoicerWeb;
    }
    if( strInvoicerTaxid !== "" ){
      request2Obj.invoicer.tax_id   = strInvoicerTaxid;
    }
    if( strInvoicerAddit !== "" ){
      request2Obj.invoicer.additional_notes = strInvoicerAddit;
    }
    request2Obj.primary_recipients = []; // required (length must be 1 in this addon)
    request2Obj.primary_recipients[0] = {};
    request2Obj.primary_recipients[0].billing_info = {};
    request2Obj.primary_recipients[0].billing_info.email_address = strRecipientEmail;
    if( strRecipientLang !== "" ){
      request2Obj.primary_recipients[0].billing_info.language = strRecipientLang;
    }
    if( strRecipientBizname !== "" ){
      request2Obj.primary_recipients[0].billing_info.business_name = strRecipientBizname;
    }
    request2Obj.primary_recipients[0].billing_info.name = {};
    if( strRecipientName1 !== "" ){
      request2Obj.primary_recipients[0].billing_info.name.given_name = strRecipientName1;
    }
    if( strRecipientName2 !== "" ){
      request2Obj.primary_recipients[0].billing_info.name.surname = strRecipientName2;
    }
    if( arrAdditionalRecipients.length > 0 ){
      request2Obj.additional_recipients = arrAdditionalRecipients;
    }
    request2Obj.items = []; // "items" array required in PayPal API
    for( let i = 0; i < arrTsvItems.length; i++ ){
      let arrColumns = arrTsvItems[i].split("\t");
      if (
       arrColumns.length < 3 ||
       arrColumns[0].trim() === "" ||
       arrColumns[1].trim() === "" ||
       arrColumns[2].trim() === ""
      ) {
       throw new Error(
        "TSV " + (i + 1) + "行目: 商品名・数量・単価は必須です"
       );
      }
      request2Obj.items[i] = {};
      request2Obj.items[i].name = arrColumns[0];     // required
      request2Obj.items[i].quantity = arrColumns[1].replace(/,/g, ""); // required
      request2Obj.items[i].unit_amount = {};
      request2Obj.items[i].unit_amount.currency_code = strCurrencycode;
      request2Obj.items[i].unit_amount.value = arrColumns[2].replace(/,/g, ""); // required

      if( arrColumns.length >= 4 ){
        request2Obj.items[i].description = arrColumns[3];
      }
      if( arrColumns.length >= 6 ){
        if( arrColumns[4] !== "" && arrColumns[5] !== "" ){
          request2Obj.items[i].tax = {};
          request2Obj.items[i].tax.name = arrColumns[4];
          request2Obj.items[i].tax.percent = arrColumns[5];
        }
      }
      if( arrColumns.length >= 7 ){
        if( arrColumns[6] !== "" ){
          request2Obj.items[i].discount = {};
          request2Obj.items[i].discount.amount = {};
          request2Obj.items[i].discount.amount.currency_code = strCurrencycode;
          request2Obj.items[i].discount.amount.value = arrColumns[6].replace(/,/g, "");
        }
      }
      if( arrColumns.length >= 8 ){
        if( arrColumns[6] !== "" && arrColumns[7] !== "" ){
          throw new Error( "\n AutomatedTask UnexpectedTsvError:" +
                           " Cannot discount by both Amount and Percent: " + i + "\n" );
        }
        if( arrColumns[7] !== "" ){
          request2Obj.items[i].discount = {};
          request2Obj.items[i].discount.percent = arrColumns[7];
        }
      }
    }
    request2Obj.configuration = {};
    request2Obj.configuration.tax_inclusive = boolTaxinclusive;
let postUri2 = "https://api-m.paypal.com/v2/invoicing/invoices";
let request2 = httpClient.begin(); // HttpRequestWrapper
    request2 = request2.bearer( strBearerToken );
    request2 = request2.body( JSON.stringify( request2Obj ), "application/json" );
engine.log( " AutomatedTask ApiRequest2 Prepared" );

// try request2
const response2 = request2.post( postUri2 ); // HttpResponseWrapper
engine.log( " AutomatedTask ApiRequest2 Start: " + postUri2 );
const response2Code = response2.getStatusCode() + ""; // (primitive string)
const response2Body = response2.getResponseAsString() + "";
engine.log( " AutomatedTask ApiResponse2 Status: " + response2Code );
if( response2Code !== "201"){
  throw new Error( "\n AutomatedTask UnexpectedResponseError: " +
                    response2Code + "\n" + response2Body + "\n" );
}
// https://developer.paypal.com/docs/api-basics/HTMLStatusCodes/

// parse response2
/*** engine.log( response2Body ); // debug
response sample
{
  "rel":"self",
  "href":"https://api.paypal.com/v2/invoicing/invoices/INV2-XXXX-8H2Z-XXXX-YGSU",
  "method":"GET"
}
***/
const response2Obj = JSON.parse( response2Body );


// prepare request3: Show invoice details
const selfLink = (response2Obj.links || [])
  .find(link => link.rel === "self");

let getUri3 = response2Obj.href
  || (selfLink && selfLink.href)
  || (response2Obj.id
      ? postUri2 + "/" + encodeURIComponent(response2Obj.id)
      : null);

if (!getUri3) {
  throw new Error("請求書作成レスポンスから詳細取得URLを取得できません");
}

let request3 = httpClient.begin(); // HttpRequestWrapper
    request3 = request3.bearer( strBearerToken );
engine.log( " AutomatedTask ApiRequest3 Prepared" );

// try request3
const response3 = request3.get( getUri3 ); // HttpResponseWrapper
engine.log( " AutomatedTask ApiRequest3 Start: " + getUri3 );
const response3Code = response3.getStatusCode() + ""; // (primitive string)
const response3Body = response3.getResponseAsString() + "";
engine.log( " AutomatedTask ApiResponse3 Status: " + response3Code );
if( response3Code !== "200"){
  throw new Error( "\n AutomatedTask UnexpectedResponseError: " +
                    response3Code + "\n" + response3Body + "\n" );
}

// parse response3
/*** engine.log( response3Body ); // debug
response sample
{
  "id":"INV2-XXXX-ZDDH-XXXX-RAJ4",
  "status":"DRAFT",
  "detail":{
    "currency_code":"JPY",
    "note":"“Questetra BPM Suite” は、....",
    "memo":"BPM 1361963",
    "additional_data":"6000835 京都市中京区高宮町204御池ビル4階",
    "category_code":"SHIPPABLE",
    "invoice_number":"20210118-1361963",
    "invoice_date":"2021-01-17",
    "payment_term":{
      "term_type":"DUE_ON_DATE_SPECIFIED",
      "due_date":"2021-04-30"
    },
    "viewed_by_recipient":false,
    "group_draft":false,
    "metadata":{
      "create_time":"2021-01-18T07:00:03Z",
      "last_update_time":"2021-01-18T07:00:03Z",
      "created_by_flow":"REGULAR_SINGLE",
      "recipient_view_url":"https://www.paypal.com/invoice/p/#XXXXZDDHXXXXRAJ4",
      "invoicer_view_url":"https://www.paypal.com/invoice/details/INV2-XXXX-ZDDH-XXXX-RAJ4",
      "caller_type":"API_V2_INVOICE"
    },
    "archived":false
  },
  "invoicer":{ ◆ },
  "primary_recipients":[{ ◆ }],
  "additional_recipients":[ "example1@example.net", "example2@example.com" ],
  "items":[{ ◆ },{ ◆ },{ ◆ }],
  "configuration":{
    "tax_calculated_after_discount":false,
    "tax_inclusive":false,
    "allow_tip":false,
    "template_id":"TEMP-137273103M373943N"
  },
  "amount":{ ◆ },
  "due_amount":{ ◆ },
}
***/
const response3Obj   = JSON.parse( response3Body );
let strInvoiceid     = response3Obj.id;
let strInvoiceurl    = response3Obj.detail?.metadata?.invoicer_view_url ?? "";
let strInvoiceStatus = response3Obj.status;


//// == Data Updating / ワークフローデータへの代入 ==
if( strPocketInvoiceid !== null ){
  engine.setData( strPocketInvoiceid,     strInvoiceid );
}
if( strPocketInvoiceurl !== null ){
  engine.setData( strPocketInvoiceurl,    strInvoiceurl );
}
if( strPocketInvoiceStatus !== null ){
  engine.setData( strPocketInvoiceStatus, strInvoiceStatus );
}

} //////// END "main()" /////////////////////////////////////////////////////////////////

Download

2026-11-06 (C) Questetra, Inc. (MIT License)
https://support.questetra.com/en/addons/paypal-invoice-create-draft-2026/
The Addon-import feature is available with Professional edition.

Notes

  • When the Case reaches this automated step in the workflow, a draft invoice is automatically created in PayPal
  • Configuring this Add-on (automated step) requires a CLIENT-ID and SECRET (obtained from the Dashboard)
  • This automated step uses the PayPal Invoicing API v2 to create a draft invoice
    • The status at creation is DRAFT
    • The invoice is not sent
    • A separate “Send” action is required to deliver it to the recipient
  • An “Invoice Draft” generally becomes payable when it is sent (or notified) to the recipient by PayPal via email
    • After processing by this Add-on, the Invoice Status will be DRAFT
    • Other Invoice Status values: DRAFT, SCHEDULED, SENT, PAID, etc.

Capture

Creates a draft invoice on the payment platform PayPal. To move the invoice from a draft to payable state, the SEND action is required. Billing automation and paperless operations are realized.
An invoice will be automatically generated when a process reaches this step in the workflow.

Appendix

  • Set the “Invoice Number” so that it does not duplicate past numbers
    • e.g. #{processInstanceId}
    • e.g. #{#format(processInstanceStartDatetime, 'yyyyMMdd')}-#{processInstanceId}
    • e.g. #{#sformat('%06d', processInstanceSequenceNumber)}
    • If left blank, the numeric portion of the most recent invoice number is automatically incremented
  • Use a locale code when specifying the language for the PayPal notification email
  • Use a currency code when specifying the currency
  • Item information is specified as TSV (tab-separated values) text
    • Up to 8 columns per TSV row can be configured:
      • ItemName / Qty / UnitAmount / Description / TaxName / TaxPercent / DiscountAmount / DiscountPercent
      • The first 3 columns (ItemName / Qty / UnitAmount) are required
    • Qty, UnitAmount, and DiscountAmount can also be specified with comma-separated digit formatting
    • Different tax rates must have different tax names (e.g. 10%: “Sales Tax”, 5%: “Reduced Tax”)
    • In cases with mixed tax rates, enter the applicable tax rate information in “ItemName” or “Description” so that it can be identified
    • DiscountAmount (column 7) and DiscountPercent (column 8) cannot be specified simultaneously
      • When specifying one, leave the other blank
  • Date-type Data Items can also be referenced for date settings
    • Format: “YYYY-MM-DD”
  • Up to 100 CC notification addresses other than the recipient can be specified (additional_recipients)
  • The invoicer’s logo image (specified by URL) must be 250x90px maximum
  • Tax ID is an optional field to specify the invoicer’s tax identification number
    • You can specify a tax registration number or equivalent
    • However, its display on the invoice depends on PayPal’s settings
  • This Add-on does not support generating multiple invoices simultaneously
  • Settings configured in the “Templates” within the invoicing tool are not applied
  • Specifying a “Shipping address” is not supported
  • The invoicer’s “Address,” “Phone number,” “Fax,” etc. are not supported
    • To include address information on the invoice, use “INVOICER’s AdditionalInfo” (B2) [max 400 characters]
    • Alternatively, use “Note to Recipient” (F) [max 4,000 characters] at the bottom of the invoice (also displayed in the notification email)
  • To perform API communication in Sandbox mode (instead of Live mode) for testing:
    • Replace the access URLs in the script with “api-m.sandbox.paypal.com” (‘postUri1’ and ‘postUri2’)
      • The service task definition file “PayPal-Invoice-Create-Draft-2026-for-sandbox.xml” listed in the Download section has already been rewritten
    • In the automated step settings, set A1 with the Sandbox CLIENT-ID and SECRET
      • Obtain the Sandbox CLIENT-ID and SECRET by creating an App in Sandbox mode in the PayPal Developer Dashboard
  • PayPal API Reference /v2/invoicing/invoices

TSV configuration example for item information (columns are tab-separated)

Service A	2	1200	Basic fee	Sales Tax	10	10	
Product B 1 800 Reduced rate Reduced Tax 5 5

See also