Overview

EnergyLink offers a rich set of Web APIs for sending or receiving Revenue & JIB statement data and automating file download workflows.

Contact your account representative about pricing to add this service to your EnergyLink subscription.

Web APIs can only be accessed via a Service Account user. A User ID and Password will be provided by EnergyLink Support during implementation. This is a service account only and cannot log into the website using a web browser. A separate user account can be provided if you need to log into the site for testing purposes. Each interface is available in both the Production and Demo environments, but a separate service account is used in each environment.

EnergyLink Website APIs

EnergyLink's REST APIs provide robust and flexible API access to transfer EnergyLink data into any database through custom developed automation.

Programmatically access EnergyLink Revenue & JIB datasets through REST APIs.

Available EnergyLink Datasets
Operated
  • Operator File Uploads – Operators can upload monthly Revenue & JIB files via SFTP or Web APIs.
    • Upload APIs are included with EnergyLink Owner Relations Hosting subscription.
    • Email notifications can be configured to notify an operator when monthly files are successfully processed.
  • Operator AR Subledger - Canadian Operators can use Web APIs to retrieve operated AR information for AR Subledger module.
    • AR API is included with an AR Subledger subscription for Canadian clients only.
    • This API gets the results of the Org Summary tab on the existing AR Subledger XLS Management report.
  • Operator ACH/COA Inquiries - Operators using the ACH/COA module can download electronic payment and change of address details submitted by their owners in JSON format via API calls (instead of CSV or XLS files via SFTP file transfer).
    • Inquiries APIs are included with the ACH/COA module
Non-Operated
  • Non-Operated File Downloads – Customers can download custom JIB vouchers or Revenue data files (example: CDEX or Excel files) based on the file format configured for your company.
    • Voucher APIs are included with an EnergyLink Foundations subscription.
    • Upon download, the invoice or check status will change to "Downloaded" in EnergyLink website.
  • Non-Operated Reporting – Customers can search for invoices or checks and automatically download statement detail in raw data JSON format. This does not change the download status in EnergyLink.
    • Reporting APIs are NOT included with EnergyLink Foundations subscription. Contact your account representative for pricing information.
    • Invoice/Payment Search: Queries high-level information about multiple invoices or revenue statements.
    • Invoice/Payment Details: Queries detailed information about a single invoice or revenue statement.
      • Statement Details may include properties matched to Well API10 numbers (ONLY IF your EnergyLink subscription includes well matching).
  • Non-Operated Workflow
    • Workflow APIs are NOT included with EnergyLink Foundations subscription. Contact your account representative for pricing information.
    • Workflow APIs can be used to automate tasks related to invoices or check processing. Examples below.
      • Validate whether the invoice or check is ready for download
      • Complete missing coding
      • Dispute invalid charges or the entire invoice
      • Approve an invoice
      • Assign a voucher reference and queue the voucher for creation

Operated File Uploads

Data files may be uploaded to EnergyLink using one of several interfaces. Successful transmission does not indicate that the data was processed and validated successfully, which can take time. Emails can be configured to notify the sender whether or not a file was successfully processed.

A User ID and Password will be provided by EnergyLink Support. This is a service account only and cannot log into the website using a web browser. A separate user account can be provided if you need to log into the site for testing purposes. Each interface is available in both the Production and Demo environments but a separate service account is used in each.

SFTP Recommended for Clients

  • Connect to
    • Production: sftp.energylink.com, port 22 (SFTP)
    • Demo: sftp-demo.energylink.com, port 22 (SFTP)
  • Zipping files before upload is highly recommended.
  • Several types of files are accepted:
    • JIB CSV - This is the default. Simply upload the ZIP or CSV file, the accounting month will be determined from the file contents.
    • JIB XML - Specify by changing to the 'JIBXML' directory. Simply upload the ZIP or XML file, the accounting month will be determined from the file contents.
    • Revenue Check Detail
      • Specify by changing to the 'REVENUE' directory and/or uploading a filename which includes the '^' character.
      • For file formats requiring the Operated Org number, specify filename as ORGNUMBER^yyyyMM.ext (e.g. 0025^201308.zip)
        • If the '^' character is not supported you may use '_' instead but then must change to the 'REVENUE' directory as noted above.
      • Accounting Month is specified using the yyyyMM above. If not provided, the current month is used.
      • If it is not possible to use the above syntax we can work with you to implement custom rules for your account(s).
    • AR Balances - Specify by changing to the 'AR' directory before uploading the file.
    • Desk Audit Land Extract - Specify by changing to the 'DESKAUDIT' directory before uploading the file.
    • Void Checks - Specify by changing to the 'VOIDCHECK' directory before uploading the file. Contact EnergyLink Support for details on the required file format.

Web API Recommended for Vendors

When using REST, the Authentication header is always required as follows:
  • Concatenate the User ID and Password using a colon, e.g. "USERID:PASSWORD"
  • Base-64 encode this string, e.g. "VVNFUklEOlBBU1NXT1JE==". This can be done here.
  • Use Basic Authentication in the "Authorization" header, e.g. "Basic VVNFUklEOlBBU1NXT1JE=="
  • Note: If testing in testing environment use https://api-demo.energylink.com in the URL

REST APIs: Please see this page for details about EnergyLink's REST APIs.

RdWebApi: When using RdWebApi an instance of EnergyLinkClientUpload is always required, e.g.

	
using (var client = new EnergyLinkClientUpload(
    environment: EnergyLinkEnvironment.Test,
    loginName: "USERID",
    password: "PASSWORD")
)
{
    // Your API calls go here. See examples below.
}
            
        
RdWebApi will automatically zip files for upload if the filename does not end with .zip

JibXml

Please see EnergyLink's JibXml API Documentation for details about making this REST call.

Request

var result = await client.UploadJibXmlAsync(
	stream, //e.g. MemoryStream, FileStream, OracleBlob
	"file.xml",
	new DateTime(2018, 11, 30, 12, 0, 0, DateTimeKind.Local)
);
    
	

JibCsv

Please see EnergyLink's JibCsv API Documentation for details about making this REST call.

Request

var result = await client.UploadJibCsvAsync(
	stream, //e.g. MemoryStream, FileStream, OracleBlob
	"file.xml",
	new DateTime(2018, 11, 30, 12, 0, 0, DateTimeKind.Local)
);
    
	

Revenue

Please see EnergyLink's Revenue API Documentation for details about making this REST call.

Request

var result = await client.UploadRevenueAsync(
	stream, //e.g. MemoryStream, FileStream, OracleBlob
	"file.xml",
	new DateTime(2018, 11, 30, 12, 0, 0, DateTimeKind.Local),
	"ORG1", 
	"EFT" // Some file formats include Org Num and Payment Type in the file, otherwise include it here
);
    
	

VoidCheck

Please see EnergyLink's VoidCheck API Documentation for details about making this REST call.

Request

var result = await client.UploadVoidCheckAsync(
	stream, //e.g. MemoryStream, FileStream, OracleBlob
	"file.xml"
);
    
	

DeskAuditXml

Please see EnergyLink's DeskAuditXml API Documentation for details about making this REST call.

Request

var result = await client.UploadDeskAuditXmlAsync(
	stream, //e.g. MemoryStream, FileStream, OracleBlob
	"file.xml"
);
    
	

Ar

Please see EnergyLink's Ar API Documentation for details about making this REST call.

Request

var result = await client.UploadArBalancesAsync(
	stream, //e.g. MemoryStream, FileStream, OracleBlob
	"file.xml"
);
    
	

Web API

EnergyLink is now offering Web APIs to retrieve operated AR information.

A User ID and Password will be provided by EnergyLink Support. This is a service account only and cannot log into the website using a web browser. A separate user account can be provided if you need to log into the site for testing purposes. Each interface is available in both the Production and Demo environments but a separate service account is used in each.

When using REST, the Authentication header is always required as follows:
  • Concatenate the User ID and Password using a colon, e.g. "USERID:PASSWORD"
  • Base-64 encode this string, e.g. "VVNFUklEOlBBU1NXT1JE==". This can be done here.
  • Use Basic Authentication in the "Authorization" header, e.g. "Basic VVNFUklEOlBBU1NXT1JE=="
  • Note: If testing in testing environment use https://api-demo.energylink.com in the URL

REST APIs: Please see this page for details about EnergyLink's REST APIs.

GetManagementReportOrgSummary

Queries the results of the Org Summary tab on the existing AR Subledger XLS Management report.

Please see EnergyLink's GetManagementReportOrgSummary API Documentation for details about making this REST call.

Request

using (var client = new EnergyLinkClientOp(
	environment: EnergyLinkEnvironment.Test,
	loginName: "USERID",
	password: "PASSWORD")
)
{
	var result = client.GetManagementReportOrgSummaryAsync(
		new DateTime(2023, 11, 1),
		new DateTime(2024, 8, 1)
	);
}

	
Response

All values are automatically deserialized into .Net classes within the ArManagementDataElement class. Happy coding!

Inquiry Automation File Downloads

The following methods may be used to move 'Created' inquiries (including ACH and COA) and their related files from EnergyLink to the client's environment. Upon download the status will change to 'Downloaded' and the inquiry will no longer be returned in the 'Inquiries for Download' queries. An inquiry can be set from 'Downloaded' back to 'Created' by manually re-creating it via the website.

By default users must create inquiries using the EnergyLink website.

It is possible to have custom fields added to the response as needed. This will have to be set up by the EnergyLink support team.

Web API Polling Recommended for Vendors

A User ID and Password will be provided by EnergyLink Support. This is a service account only and cannot log into the website using a web browser. A separate user account can be provided if you need to log into the site for testing purposes. Each interface is available in both the Production and Demo environments but a separate service account is used in each.

When using REST, the Authentication header is always required as follows:
  • Concatenate the User ID and Password using a colon, e.g. "USERID:PASSWORD"
  • Base-64 encode this string, e.g. "VVNFUklEOlBBU1NXT1JE==". This can be done here.
  • Use Basic Authentication in the "Authorization" header, e.g. "Basic VVNFUklEOlBBU1NXT1JE=="
  • Note: If testing in testing environment use https://api-demo.energylink.com in the URL

REST APIs: Please see this page for details about EnergyLink's REST APIs.

GetAchExport

Returns the unprocessed owner inquiries for changes to their electronic payment information. When isTesting=False, receiving these will mark the inquiry as processed on EnergyLink

Please see EnergyLink's GetAchExport API Documentation for details about making this REST call.

Request

using (var client = new EnergyLinkClientInquiry(
	environment: EnergyLinkEnvironment.Test,
	loginName: "USERID",
	password: "PASSWORD")
)
{
	var result = await client.AchExport;
}
		
	

GetCoaExport

Returns the unprocessed owner inquiries for changes to their address information. When isTesting=False, receiving these will mark the inquiry as processed on EnergyLink

Please see EnergyLink's GetCoaExport API Documentation for details about making this REST call.

Request

using (var client = new EnergyLinkClientInquiry(
	environment: EnergyLinkEnvironment.Test,
	loginName: "USERID",
	password: "PASSWORD")
)
{
	var result = await client.GetCoaExport;
}
		
	

InquiryOwnerInfoCsv

Please see EnergyLink's InquiryOwnerInfoCsv API Documentation for details about making this REST call.

Request

var result = await client.UploadInquiryOwnerInfoCsvAsync(
	stream, //e.g. MemoryStream, FileStream, OracleBlob
	"file.csv.zip"
);
    
	

CloseAchs

Please see EnergyLink's CloseAchs API Documentation for details about making this REST call.

Request

var items = new List<AssignVoucherReferencesRequestItem>
{
	new AssignVoucherReferencesRequestItem
	{
		InquiryId = 123,
		CloseInquiryMessage = "Some message",
	}
};
var result = await client.UploadInquiryCloseRequestJsonAsync(items);
var resultContent = await result.Content.ReadAsStringAsync();   //resultContent is a list of InquiryIds and the message
	
    

CloseCoas

Please see EnergyLink's CloseCoas API Documentation for details about making this REST call.

Request

var items = new List<AssignVoucherReferencesRequestItem>
{
	new AssignVoucherReferencesRequestItem
	{
		InquiryId = 123,
		CloseInquiryMessage = "Some message",
	}
};
var result = await client.UploadInquiryCloseRequestJsonAsync(items);
var resultContent = await result.Content.ReadAsStringAsync();   //resultContent is a list of InquiryIds and the message
	
    

Non-Operated File Downloads

The following methods may be used to move 'Created' vouchers and their related files from EnergyLink to the client's environment. Upon download the status will change to 'Downloaded' and the voucher will no longer be returned in the 'Vouchers for Download' queries. A voucher can be set from 'Downloaded' back to 'Created' by manually re-creating it via the website.

By default users must create vouchers using the EnergyLink website. In some scenarios vouchers can be created automatically as data is received (e.g. if only operator coding is required).

Numerous additional options exist, depending on file format (e.g. CDEX). Please contact us to discuss your specific requirements.

For information about automating the steps to process an invoice/check and create the voucher (e.g. using RPA software), see the Non-Operated Workflow section.

SFTP / FTP (Push) Recommended for Clients

EnergyLink will transmit files to a location provided by the client as they become available.

  • Destination Address, User ID and Password must be provided by the client (this may differ between Production and Demo).
  • Consolidated CDEX - For CDEX files only, all files may be grouped into a single transmission sent once per day.
  • Whitelist the following IP addresses:
    • 161.38.183.10
    • 199.231.63.10
  • The destination folder may be monitored by a scheduled job or certain enterprise software for upload into the client's system.
  • Failed transfers will be retried 4 times, 15 minutes apart before giving up. Unsuccessful transfers may be found under 'Non-Operated Voucher Search'. A failed voucher may be recreated to trigger a new transfer.

SFTP (Pull)

Clients may connect to the EnergyLink FTP site periodically to list and download new files.

A User ID and Password will be provided by EnergyLink Support. This is a service account only and cannot log into the website using a web browser. A separate user account can be provided if you need to log into the site for testing purposes. Each interface is available in both the Production and Demo environments but a separate service account is used in each.

  • Connect to
    • Production: sftp.energylink.com, port 22 (SFTP)
    • Demo: sftp-demo.energylink.com, port 22 (SFTP)
  • JIBs - Change to the 'DOWNLOAD_JIB' directory.
    Revenue Check Detail - Change to the 'DOWNLOAD_REVENUE' directory.
  • Downloaded files will be automatically removed from the directory listing. They may be recreated via the website to make them reappear for download.

Web API Polling Recommended for Vendors

A User ID and Password will be provided by EnergyLink Support. This is a service account only and cannot log into the website using a web browser. A separate user account can be provided if you need to log into the site for testing purposes. Each interface is available in both the Production and Demo environments but a separate service account is used in each.

When using REST, the Authentication header is always required as follows:
  • Concatenate the User ID and Password using a colon, e.g. "USERID:PASSWORD"
  • Base-64 encode this string, e.g. "VVNFUklEOlBBU1NXT1JE==". This can be done here.
  • Use Basic Authentication in the "Authorization" header, e.g. "Basic VVNFUklEOlBBU1NXT1JE=="
  • Note: If testing in testing environment use https://api-demo.energylink.com in the URL

REST APIs: Please see this page for details about EnergyLink's REST APIs.

GetInvoiceVouchersForDownload

Please see EnergyLink's GetInvoiceVouchersForDownload API Documentation for details about making this REST call.

Request

using (var client = new EnergyLinkClientVoucher(
	environment: EnergyLinkEnvironment.Test,
	loginName: "USERID",
	password: "PASSWORD")
)
{
	var result = await client.GetInvoiceVouchersForDownloadAsync();
}
	
	
Response

All values are automatically deserialized into .Net classes within the VouchersElement class. Happy coding!

GetPaymentVouchersForDownload

Please see EnergyLink's GetPaymentVouchersForDownload API Documentation for details about making this REST call.

Request

using (var client = new EnergyLinkClientVoucher(
	environment: EnergyLinkEnvironment.Test,
	loginName: "USERID",
	password: "PASSWORD")
)
{
	var result = await client.GetPaymentVouchersForDownloadAsync();
}
	
	
Response

All values are automatically deserialized into .Net classes within the VouchersElement class. Happy coding!

GetVoucherFile

Usually you will need to call GetInvoiceVouchersForDownload first to get the Voucher Id.

Please see EnergyLink's GetVoucherFile API Documentation for details about making this REST call.

Request

using (var client = new EnergyLinkClientVoucher(
	environment: EnergyLinkEnvironment.Test,
	loginName: "USERID",
	password: "PASSWORD")
)
{
	var result = await client.GetVoucherFileStreamAsync(123);
}
	
	
Response

All values are automatically deserialized into .Net classes within the VoucherFile class. Happy coding!

GetVoucherReports

Usually you will need to call GetInvoiceVouchersForDownload first to get the Voucher Id.

Once EnergyLink enables the PDF option, the PDF backup is automatically created with each voucher. The GetVoucherReports Web API call will fetch these reports.

Please see EnergyLink's GetVoucherReports API Documentation for details about making this REST call.

Request

using (var client = new EnergyLinkClientVoucher(
	environment: EnergyLinkEnvironment.Test,
	loginName: "USERID",
	password: "PASSWORD")
)
{
	var result = await client.GetVoucherReportsAsync(123);
}
	
	
Response

All values are automatically deserialized into .Net classes within the VoucherFile class. Happy coding!

Non-Operated Reporting

EnergyLink offers a rich set of Web APIs for retrieving information about Invoices and/or Checks.

A User ID and Password will be provided by EnergyLink Support. This is a service account only and cannot log into the website using a web browser. A separate user account can be provided if you need to log into the site for testing purposes. Each interface is available in both the Production and Demo environments but a separate service account is used in each.

When using REST, the Authentication header is always required as follows:
  • Concatenate the User ID and Password using a colon, e.g. "USERID:PASSWORD"
  • Base-64 encode this string, e.g. "VVNFUklEOlBBU1NXT1JE==". This can be done here.
  • Use Basic Authentication in the "Authorization" header, e.g. "Basic VVNFUklEOlBBU1NXT1JE=="
  • Note: If testing in testing environment use https://api-demo.energylink.com in the URL

Parts of the data returned by these APIs are used by the EnergyLink Workflow APIs. For Example: Invoice ID, or Check Id.
See Non-Operated Workflow for details.

InvoiceSearch

Queries high-level information about multiple invoices. For statement-level and detail-level information see Invoice.

Please see EnergyLink's InvoiceSearch API Documentation for details about making this REST call.

Request

using (var client = new EnergyLinkClientNonOp(
	environment: EnergyLinkEnvironment.Test,
	loginName: "USERID",
	password: "PASSWORD")
)
{
	var nonOpInvoices = await client.GetInvoiceSearchAsync(
		Enums.DateSearchType.RECEIVED_DATE,
		new DateTime(2026, 3, 17, 12, 0, 0, DateTimeKind.Local),
		invoiceType: new[] { Enums.InvoiceTypes.JIB }
	);
}
	
    
Response

All values from the NonOpInvoices XML schema are automatically deserialized into .Net classes within the NonOpInvoicesElement class. Happy coding!

RevenueSearch

USA only: Queries high-level information about multiple checks. For property-level and detail-level information see Revenue.

Please see EnergyLink's RevenueSearch API Documentation for details about making this REST call.

Request

using (var client = new EnergyLinkClientNonOp(
	environment: EnergyLinkEnvironment.Test,
	loginName: "USERID",
	password: "PASSWORD")
)
{
	var nonOpRevenue = await client.GetRevenueSearchAsync(
		Enums.DateSearchType.RECEIVED_DATE,
		new DateTime(2026, 3, 17, 12, 0, 0, DateTimeKind.Local)
	);
}
	
    
Response

All values from the NonOpRevenue XML schema are automatically deserialized into .Net classes within the NonOpRevenueElement class. Happy coding!

RoyaltySearch

Canada only: Queries high-level information about multiple royalties. For property-level and detail-level information see Royalty.

Please see EnergyLink's RoyaltySearch API Documentation for details about making this REST call.

Request

using (var client = new EnergyLinkClientNonOp(
	environment: EnergyLinkEnvironment.Test,
	loginName: "USERID",
	password: "PASSWORD")
)
{
	var nonOpRoyalties = await client.GetRoyaltySearchAsync(
		Enums.DateSearchType.RECEIVED_DATE,
		new DateTime(2026, 3, 17, 12, 0, 0, DateTimeKind.Local)
	);
}
	
    
Response

All values from the NonOpRoyalties XML schema are automatically deserialized into .Net classes within the NonOpRoyaltiesElement class. Happy coding!

Invoice

Queries detailed information about a single invoice. For high-level information on multiple invoices see InvoiceSearch.

Please see EnergyLink's Invoice API Documentation for details about making this REST call.

Request

using (var client = new EnergyLinkClientNonOp(
    environment: EnergyLinkEnvironment.Test,
    loginName: "USERID",
    password: "PASSWORD")
)
{
    var nonOpInvoices = await client.GetInvoiceAsync(
        123456,
        Enums.InvoiceSummarization.Detail
    );
}
    
    
Response

All values from the NonOpInvoices XML schema are automatically deserialized into .Net classes within the NonOpInvoicesElement class. Happy coding!

Revenue

USA only: Queries detailed information about a single check. For high-level information on multiple checks see RevenueSearch.

Please see EnergyLink's Revenue API Documentation for details about making this REST call.

Request

using (var client = new EnergyLinkClientNonOp(
    environment: EnergyLinkEnvironment.Test,
    loginName: "USERID",
    password: "PASSWORD")
)
{
    var nonOpRevenue = await client.GetRevenueAsync(
        123456,
        Enums.InvoiceSummarization.Detail
    );
}
    
    
Response

All values from the NonOpRevenue XML schema are automatically deserialized into .Net classes within the NonOpRevenueElement class. Happy coding!

Royalty

Canada only: Queries detailed information about a single royalty. For high-level information on multiple royalties see RoyaltySearch.

Please see EnergyLink's Royalty API Documentation for details about making this REST call.

Request

using (var client = new EnergyLinkClientNonOp(
    environment: EnergyLinkEnvironment.Test,
    loginName: "USERID",
    password: "PASSWORD")
)
{
    var nonOpRoyalty = await client.GetRoyaltyAsync(
        123456,
        Enums.InvoiceSummarization.Detail
    );
}
    
    
Response

All values from the NonOpRoyalties XML schema are automatically deserialized into .Net classes within the NonOpRoyaltiesElement class. Happy coding!

Non-Operated Automation

EnergyLink offers a rich set of Web APIs for retrieving information and automating workflow. The RdWebApi package is strongly recommended for the latter.

A User ID and Password will be provided by EnergyLink Support. This is a service account only and cannot log into the website using a web browser. A separate user account can be provided if you need to log into the site for testing purposes. Each interface is available in both the Production and Demo environments but a separate service account is used in each.

When using REST, the Authentication header is always required as follows:
  • Concatenate the User ID and Password using a colon, e.g. "USERID:PASSWORD"
  • Base-64 encode this string, e.g. "VVNFUklEOlBBU1NXT1JE==". This can be done here.
  • Use Basic Authentication in the "Authorization" header, e.g. "Basic VVNFUklEOlBBU1NXT1JE=="
  • Note: If testing in testing environment use https://api-demo.energylink.com in the URL

The first step is to search for documents and download their details. See Non-Operated Reporting for details. EnergyLink primary key values will be included with this data - store and refer to these keys when making subsequent Web API calls. See the Samples section for examples of multiple Web APIs used together to complete more complex tasks.

EnergyLinkClientNonOp

When using RdWebApi an instance of EnergyLinkClientNonOp is always required, e.g.

	
using (var client = new EnergyLinkClientNonOp(
    environment: EnergyLinkEnvironment.Test,
    loginName: "USERID",
    password: "PASSWORD")
)
{
    // Your API calls go here. See examples below.
}
    
	

ValidateInvoice

Identifies all issues preventing this invoice from being vouchered (e.g. missing approval).

Please see EnergyLink's ValidateInvoice API Documentation for details about making this REST call.

Request

	var result = await client.ValidateInvoiceAsync(123);
	
	

CreateAfeXrefs

Please see EnergyLink's CreateAfeXrefs API Documentation for details about making this REST call.

Request

var afeElements = new List<AfeElement>
{
    new AfeElement { 
		AfeId = 123, 
		PrtAfeNumber = "AFE123",								// Optional
		PrtCostCenterCode = "AFECC123",							// Optional
		PrtAfeActionCode = Enums.AfeActionCodes.DisputeSold		// Optional
	}
};
var result = await client.CreateAfeXrefsAsync(afeElements);
	
	

CreateCcXrefs

Please see EnergyLink's CreateCcXrefs API Documentation for details about making this REST call.

Request

var ccElements = new List<CostCenterElement>
{
    new CostCenterElement { 
		CcId = 123, 
		PrtAfeNumber = "AFE123",										// Optional
		PrtCostCenterCode = "CC123",									// Optional
		PrtCostCenterActionCode = Enums.CcActionCodes.DisputeSold		// Optional
	}
};
var result = await client.CreateCcXrefsAsync(ccElements);
	
	

UpdateAfeXrefs

Please see EnergyLink's UpdateAfeXrefs API Documentation for details about making this REST call.

Request

var afeElements = new List<AfeElement>
{
    new AfeElement { 
		AfeId = 123, 
		AfeXrefId = 456,
		PrtAfeNumber = "AFE123",								// Optional
		PrtCostCenterCode = "AFECC123",							// Optional
		PrtAfeActionCode = Enums.AfeActionCodes.DisputeSold		// Optional
	}
};
var result = await client.UpdateAfeXrefsAsync(afeElements);
	
	

UpdateCcXrefs

Please see EnergyLink's UpdateCcXrefs API Documentation for details about making this REST call.

Request

var ccElements = new List<CostCenterElement>
{
    new CostCenterElement { 
		CcId = 123, 
		CcXrefId = 456,
		PrtAfeNumber = "AFE123",										// Optional
		PrtCostCenterCode = "CC123",									// Optional
		PrtCostCenterActionCode = Enums.CcActionCodes.DisputeSold		// Optional
	}
};
var result = await client.UpdateCcXrefsAsync(ccElements);
	
	

UpdateStatementDisputes

Please see EnergyLink's UpdateStatementDisputes API Documentation for details about making this REST call.

Request

var statementDispute = new StatementDisputeElement
{
	DisputeReason = Enums.DisputeReasonCode.Sold,
	DisputeComment = "Our records indicate that we have sold this property",
	DisputeEntireStatement = true
};
var result = await EnergyLinkClient.UpdateStatementDisputesAsync(12345, statementDispute);
	
	

ApproveInvoices

Please see EnergyLink's ApproveInvoices API Documentation for details about making this REST call.

Request

var invoiceIds = new List<long> { 12345, 12346 };
var result = await client.ApproveInvoicesAsync(invoiceIds);
    
	

ApproveStatement

Please see EnergyLink's ApproveStatement API Documentation for details about making this REST call.

Request

//optional. you can pass a null as the 2nd parameter to approve the entire statement
var statementApproval = new StatementApprovalElement
{
	ExceptDetailIds = new List<long> { 12346 }
};
var result = await client.ApproveStatementAsync(12345, statementApproval);
    
	

AssignVoucherReferences

Please see EnergyLink's AssignVoucherReferences API Documentation for details about making this REST call.

Request

var items = new List<AssignVoucherReferencesRequestItem>
{
	new AssignVoucherReferencesRequestItem
	{
		InvoiceId = 123,
		VoucherReference = "VCH123",
		VoucherAccountingMonth = new DateTime(2018, 11, 30, 12, 0, 0, DateTimeKind.Local)
	}
};
var result = await client.AssignVoucherReferencesAsync(items);
var resultContent = await result.Content.ReadAsStringAsync();
//resultContent is a list of InvoiceIds and their new VoucherIds
var responseItems = JsonConvert.DeserializeObject<List<EnergyLinkClientNonOp.AssignVoucherReferencesResponseItem>>(content);
	
    

ValidateVoucher

Identifies all issues preventing this voucher from being created (e.g. missing approval).

Please see EnergyLink's ValidateVoucher API Documentation for details about making this REST call.

Request

	var result = await client.ValidateVoucherAsync(123);
	
	

QueueVoucher

Attempt to queue the voucher for creation. When finished the file will be available via the methods outlined under Non-Operated File Downloads.

Please see EnergyLink's QueueVoucher API Documentation for details about making this REST call.

Request

	var result = await client.QueueVoucherAsync(123);
	
	

Non-Operated Automation Samples


The following sample searches for invoices received within the past 2 days and attempts to:

  • Complete missing coding
  • Dispute invalid charges
  • Validate whether the invoice is ready for download
  • Approve the invoice
  • Assign a voucher reference and queue the voucher for creation
Simply include RdWebApi in your project, create a class inheriting from InvoiceProcessingBot, then run ProcessInvoices(). See Non-Operated File Downloads for options to automatically download the voucher file and PDF backup.

	
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using RedDog.RdWebApi.NonOp;

namespace RedDog.RdWebApi.Samples
{
	public abstract class InvoiceProcessingBot
	{
		public abstract EnergyLinkEnvironment EnergyLinkEnvironment { get; }
		public abstract string EnergyLinkLoginName { get; }
		public abstract string EnergyLinkPassword { get; }
		protected virtual EnergyLinkClientNonOp EnergyLinkClient { get; set; }
		public abstract bool DoApprovals { get; }
		public abstract bool AssignVoucherRefIfApproved { get; }
		public abstract bool QueueVoucherIfVoucherRefAssigned { get; }
		
		public virtual async Task ProcessInvoices()
		{
			using (EnergyLinkClient ?? (EnergyLinkClient = new EnergyLinkClientNonOp(EnergyLinkEnvironment, EnergyLinkLoginName, EnergyLinkPassword)))
			{
				Log("Getting Invoices", 1);
				var invoiceSearch = await GetInvoiceSearch();
				foreach (var invoice in invoiceSearch.Invoices)
				{
					var invoiceWithDetail = (await EnergyLinkClient.GetInvoiceAsync(invoice.InvoiceId, Enums.InvoiceSummarization.Detail)).Invoices[0];
					await ProcessInvoice(invoiceWithDetail);
					await ApproveAndVoucherInvoice(invoiceWithDetail);
				}
			}
		}

		protected virtual async Task<NonOpInvoicesElement> GetInvoiceSearch()
		{
			using (var client = new EnergyLinkClientNonOp(EnergyLinkEnvironment, EnergyLinkLoginName, EnergyLinkPassword))
			{
				//get a list of JIBs received in the past 2 days still in Received or Viewed status
				return await client.GetInvoiceSearchAsync(
					Enums.DateSearchType.RECEIVED_DATE, 
					DateTime.Today.AddDays(-2), 
					invoiceType: new []{ Enums.InvoiceTypes.JIB }, 
					status: new []{ Enums.InvoiceStatusCodes.Received, Enums.InvoiceStatusCodes.Viewed }
				);
			}
		}

		#region Process Invoice
		protected virtual async Task ProcessInvoice(InvoiceElement invoice)
		{
			Log($"Processing {invoice.Operator.OrgName} Invoice {invoice.InvoiceNumber}", 1);

			var newAfeXrefs = new Dictionary<long, AfeElement>();
			var newCcXrefs = new Dictionary<long, CostCenterElement>();
			
			foreach (var statement in invoice.Statements)
			{
				await ProcessStatement(statement);

				if (!statement.AcceptedPartnerAmount.HasValue && !statement.AcceptedGstAmount.HasValue && !statement.AcceptedCashCallAmount.HasValue)
				{
					//Mapping is not required for fully disputed statements
					continue;
				}

				if (statement.Afe != null && !statement.Afe.AfeXrefId.HasValue)
				{
					if (TryMapAfe(statement.Afe) && !string.IsNullOrWhiteSpace(statement.Afe.PrtAfeNumber))
					{
						newAfeXrefs.Add(statement.Afe.AfeId, statement.Afe);
					}	
				}

				if (statement.CostCenter != null && !statement.CostCenter.CcXrefId.HasValue)
				{
					if (TryMapCostCenter(statement.CostCenter) && !string.IsNullOrWhiteSpace(statement.CostCenter.PrtCostCenterCode))
					{
						newCcXrefs.Add(statement.CostCenter.CcId, statement.CostCenter);
					}	
				}
			}

			if (newAfeXrefs.Count > 0)
			{
				foreach (var newAfeXref in newAfeXrefs.Values)
				{
					Log($"OpAfeNumber '{newAfeXref.OpAfeNumber}' will be mapped to '{newAfeXref.PrtAfeNumber}'");
				}
				var result = await EnergyLinkClient.CreateAfeXrefsAsync(newAfeXrefs.Values.ToList());
				if (!result.IsSuccessStatusCode) throw new InvalidOperationException($"Error mapping {newAfeXrefs.Count} AFEs: {result.ReasonPhrase}");
				Log($"Mapped {newAfeXrefs.Count} AFEs");
			}
			
			if (newCcXrefs.Count > 0)
			{
				foreach (var newCcXref in newCcXrefs.Values)
				{
					Log($"OpCostCenterCode '{newCcXref.OpCostCenterCode}' will be mapped to '{newCcXref.PrtCostCenterCode}'");
				}
				var result = await EnergyLinkClient.CreateCcXrefsAsync(newCcXrefs.Values.ToList());
				if (!result.IsSuccessStatusCode) throw new InvalidOperationException($"Error mapping {newCcXrefs.Count} CostCenters: {result.ReasonPhrase}");
				Log($"Mapped {newCcXrefs.Count} CostCenters");
			}
		}

		protected virtual bool TryMapAfe(AfeElement afe)
		{
			//Implement logic here to set the PrtAfeNumber (and optionally the PrtCostCenterCode, PrtAfeActionCode)
			return false;
		}

		protected virtual bool TryMapCostCenter(CostCenterElement cc)
		{
			//Implement logic here to set the PrtCostCenterCode (and optionally the PrtAfeNumber, PrtCostCenterActionCode)
			return false;
		}

		protected virtual async Task ProcessStatement(StatementElement statement)
		{
			//Hint: Clients using Desk Audit can easily set their 'Sold' and 'No Interest'
			//property flags online based on data from the land system
			switch (statement.CostCenter?.PrtCostCenterActionCode)
			{
				case Enums.CcActionCodes.DisputeNoInterest:
					if (await DisputeNoInterest(statement)) return;
					break;
				case Enums.CcActionCodes.DisputeSold:
					if (await DisputeSold(statement)) return;
					break;
			}

			//If we are not rejecting the property outright then continue onto possible detail-level disputes.
			//Note: There can only be one Dispute Reason and Dispute Comment
			//		per statement so pick the "most important" one that applies.
			//		In this case we assume that DOI > Overhead > Self-Insured
			var statementDispute = new StatementDisputeElement
			{
				DetailDisputes = new List<DetailDisputeElement>()
			};
			
			foreach (var detail in statement.Details)
			{
				if (DisputeDoi(statement, detail, out var correctDoi))
				{
					if (!statementDispute.DisputeReason.HasValue)
					{
						statementDispute.DisputeReason = Enums.DisputeReasonCode.Wrong_DOI;
						statementDispute.DisputeComment = $"Our records indicate that this DOI should be {correctDoi}.";
					}
					statementDispute.DetailDisputes.Add(new DetailDisputeElement { DetailId = 123, AcceptedPartnerAmount = null });
				}
				else if (IsOverhead(detail) && DisputeOverhead(statement, detail, out var correctOverhead))
				{
					if (!statementDispute.DisputeReason.HasValue)
					{
						statementDispute.DisputeReason = Enums.DisputeReasonCode.Overhead_Fee;
						statementDispute.DisputeComment = $"Our records indicate that the overhead rate should be {correctOverhead}.";
					}
					statementDispute.DetailDisputes.Add(new DetailDisputeElement { DetailId = 123, AcceptedPartnerAmount = null });
				}
				else if (IsInsurance(detail) && DisputeInsurance(statement))
				{
					if (!statementDispute.DisputeReason.HasValue)
					{
						statementDispute.DisputeReason = Enums.DisputeReasonCode.Self_Insured;
						statementDispute.DisputeComment = "Our records indicate that this property is self-insured.";
					}
					statementDispute.DetailDisputes.Add(new DetailDisputeElement { DetailId = 123, AcceptedPartnerAmount = null });
				}
			}

			if (statementDispute.DisputeReason.HasValue && statementDispute.DetailDisputes.Count > 0)
			{
				var result = await EnergyLinkClient.UpdateStatementDisputesAsync(statement.StatementId, statementDispute);
				if (!result.IsSuccessStatusCode) 
					throw new InvalidOperationException($"Error updating disputes on StatementId {statement.StatementId}: {result.ReasonPhrase}");
			}

			await Task.FromResult(0);
		}

		protected virtual async Task<bool> DisputeNoInterest(StatementElement statement)
		{
			var statementDispute = new StatementDisputeElement
			{
				DisputeReason = Enums.DisputeReasonCode.NoInterest,
				DisputeComment = "Our records indicate that we have no interest in this property",
				DisputeEntireStatement = true
			};
			var result = await EnergyLinkClient.UpdateStatementDisputesAsync(statement.StatementId, statementDispute);
			if (!result.IsSuccessStatusCode) 
				throw new InvalidOperationException($"Error updating disputes on StatementId {statement.StatementId}: {result.ReasonPhrase}");
			return true;
		}

		protected virtual async Task<bool> DisputeSold(StatementElement statement)
		{
			var statementDispute = new StatementDisputeElement
			{
				DisputeReason = Enums.DisputeReasonCode.Sold,
				DisputeComment = "Our records indicate that we have sold this property",
				DisputeEntireStatement = true
			};
			var result = await EnergyLinkClient.UpdateStatementDisputesAsync(statement.StatementId, statementDispute);
			if (!result.IsSuccessStatusCode) 
				throw new InvalidOperationException($"Error updating disputes on StatementId {statement.StatementId}: {result.ReasonPhrase}");
			return true;
		}

		protected virtual bool DisputeDoi(StatementElement statement, DetailElement detail, out decimal? correctDoi)
		{
			//Implement logic here to decide if this DOI should be disputed (e.g. if it does not match your land system)
			//Hint: Clients using Desk Audit can easily get this info from the EnergyLink API calls included in this package
			correctDoi = 50;
			return false;
		}

		protected virtual bool IsOverhead(DetailElement detail)
		{
			//Hint: Compare detail.PrtMajorAccountCode and detail.PrtMinorAccountCode to your GL account(s) for overhead
			return detail.EnergyLinkMinorAccountDesc.IndexOf("OVERHEAD", StringComparison.InvariantCultureIgnoreCase) >= 0;
		} 
		
		protected virtual bool DisputeOverhead(StatementElement statement, DetailElement detail, out decimal? correctOverhead)
		{
			//Implement logic here to decide if this overhead line should be disputed (e.g. if it does not match your land system)
			//Hint: Clients using Desk Audit can easily get this info from the EnergyLink API calls included in this package
			correctOverhead = 150;
			return false;
		}

		protected virtual bool IsInsurance(DetailElement detail)
		{
			//Hint: Compare detail.PrtMajorAccountCode and detail.PrtMinorAccountCode to your GL account(s) for insurance
			return detail.EnergyLinkMinorAccountDesc.IndexOf("INSURANCE", StringComparison.InvariantCultureIgnoreCase) >= 0;
		} 
		
		protected virtual bool DisputeInsurance(StatementElement statement)
		{
			//Implement logic here to decide if insurance should be disputed on this property (e.g. if it is self-insured)
			return false;
		}
		#endregion

		#region Approve and Voucher
		protected virtual async Task ApproveAndVoucherInvoice(InvoiceElement invoice)
		{
			var validateResult = await EnergyLinkClient.ValidateInvoiceAsync(invoice.InvoiceId);
			if (validateResult.Validations.Any(x => x.Key != Enums.Validations.APPROVAL_REQD))
			{
				Log($"Validation Summary:\r\n{validateResult.ValidationSummary}");
				Log($"Invoice {invoice.InvoiceNumber} has outstanding validations - cannot continue.");
				return;
			}

			if (!invoice.IsApproved)
			{
				if (!DoApprovals)
				{
					Log("Not configured to approve invoices - done with this invoice.");
					return;
				}

				if (ApproveInvoice(invoice))
				{
					var result = await EnergyLinkClient.ApproveInvoicesAsync(new List<long> {invoice.InvoiceId});
					if (!result.IsSuccessStatusCode) throw new InvalidOperationException($"Error approving InvoiceId {invoice.InvoiceId}: {result.ReasonPhrase}");
					Log($"Invoice {invoice.InvoiceNumber} approved.");
				}
				else
				{
					Log("Did not meet criteria for approval - done with this invoice.");
					return;
				}
			}

			if (validateResult.OtherInvoicesWithValidations?.Count > 0)
			{
				Log($"Invoice {invoice.InvoiceNumber} is ready for download but InvoiceIds {string.Join(",", validateResult.OtherInvoicesWithValidations.Select(x => x.ToString()))} share the voucher and still have validations.");
				return;
			}

			if (!invoice.ActiveVoucherId.HasValue)
			{
				if (!AssignVoucherRefIfApproved)
				{
					Log("Not configured to assign voucher references - done with this invoice.");
					return;
				}

				var voucher = AssignVoucher(invoice);
				var result = await EnergyLinkClient.AssignVoucherReferencesAsync(new List<EnergyLinkClientNonOp.AssignVoucherReferencesRequestItem> {voucher});
				if (!result.IsSuccessStatusCode) throw new InvalidOperationException($"Error assigning voucher reference to InvoiceId {invoice.InvoiceId}: {result.ReasonPhrase}");
				var content = await result.Content.ReadAsStringAsync();
				var response = JsonConvert.DeserializeObject<List<EnergyLinkClientNonOp.AssignVoucherReferencesResponseItem>>(content);
				if (response?.Count != 1 || !response[0].VoucherId.HasValue) throw new InvalidOperationException($"Error assigning voucher reference to InvoiceId {invoice.InvoiceId}: No VoucherId was returned.");
				Log($"Voucher Reference {invoice.InvoiceNumber} assigned.");

				if (!QueueVoucherIfVoucherRefAssigned)
				{
					Log("Not configured to queue vouchers - done with this invoice.");
					return;
				}
				var voucherId = response[0].VoucherId.Value;
				var queueResult = await EnergyLinkClient.QueueVoucherAsync(voucherId);
				if (!queueResult.IsSuccessStatusCode) throw new InvalidOperationException($"Error queueing VoucherId {voucherId}: {result.ReasonPhrase}");
				Log($"Voucher {voucher.VoucherReference} queued for creation.");
			}
		}

		protected virtual bool ApproveInvoice(InvoiceElement invoice)
		{
			//Implement logic here to decide whether to approve an invoice for download if the validations are satisfied, e.g.:
			var invoiceAmount = (invoice.OriginalPartnerAmount ?? 0) + (invoice.OriginalGstAmount ?? 0) + (invoice.OriginalCashCallAmount ?? 0);
			return Math.Abs(invoiceAmount) <= 100;
		}

		protected virtual EnergyLinkClientNonOp.AssignVoucherReferencesRequestItem AssignVoucher(InvoiceElement invoice)
		{
			//Implement logic here to assign the voucher reference and accounting month, e.g.:
			var voucher = new EnergyLinkClientNonOp.AssignVoucherReferencesRequestItem
			{
				InvoiceId = invoice.InvoiceId,
				VoucherReference = $"{invoice.InvoiceNumber}.{invoice.InvoiceId}",
				VoucherAccountingMonth = DateTime.Today
			};
			//Note: appending InvoiceId will ensure uniqueness if using one invoice per voucher
			return voucher;
		}
		#endregion

		protected virtual void Log(string message, int tabs = 2)
		{
			Trace.WriteLine($"{DateTime.Now:yyyy-MM-dd hh:mm:ss}{string.Concat(Enumerable.Repeat("\t", tabs))}{message}");
		}
	}
}
			
		

What is RdWebApi?

Nuget allows .Net code to be shared between applications. When it comes to EnergyLink APIs the need arose internally to use a Nuget package to help manage API calls between EnergyLink and those applications. We also used the package to write automated tests for those interactions.

With many EnergyLink clients, vendors and 3rd parties now looking to use Web APIs, we decided to take it a step further and offer RdWebApi externally. We see many benefits to this approach:

  • The package takes care of the technical details (networking, serialization, compression) so you can focus on the business logic and spend less time up front
  • Inputs and outputs use strongly-typed .Net classes
  • Fixes and enhancements can be acquired quickly and easily (typically in one click)
  • Access the system the same way we do internally, ensuring your scenario is well supported (you literally use the same code as EnergyLink)
  • Some RPA Software solutions can import a Nuget package or at least the .dll file

Requirements

  • All interfaces require an active EnergyLink service account (one account per full-service company)
  • Certain roles and/or features may be required depending on usage. Contact EnergyLink Support to get set up
  • A .Net-compatible environment (e.g. Framework 4.0+, Core 2.0+, Windows Powershell, certain RPA Software)
  • The Newtonsoft.Json package is a required dependency. For .Net Framework versions below 4.7.1, System.IO.Compression is required as well.
  • Some methods are not included when using .Net Framework 4.0. Please consider upgrading to 4.5+

Where to get it

The package is available via a public Nuget repository for any full-service client. Please contact us to get the link. We would be happy to discuss your project and help you find the best solution.

In scenarios where Nuget is unsupported and a .DLL file is required, the .DLL must be extracted from the .Nuget file. .Nuget files are zip files so just change the extension to ".zip" and browse the file for the appropriate .DLL.

How to use it

Each category of APIs (Operated File Uploads, Non-Operated File Downloads, Non-Operated Automation) has a different 'EnergyLinkClient' class. Once this has been identified, most Web API calls take two lines of code, e.g.:

	
using (var client = new EnergyLinkClientUpload(
    environment: EnergyLinkEnvironment.Test,
    loginName: "USERID",
    password: "PASSWORD")
)
{
    var result = await client.UploadJibXmlAsync(inputStream, "Invoices.xml");
}
            
        

Examples are provided for each Web API call elsewhere on this page

Integrating with RPA software

Some clients have expressed an interest in calling Web APIs from commercially available RPA software. Many of these products can integrate with the RdWebApi package - details for some of these products are noted below. Please contact us if you'd like to discuss your RPA project.

Automation Anywhere

While Nuget packages cannot be imported directly, .Net DLLs can be used by custom metabots. For information see this example video.

Blue Prism

Blue Prism can reference a custom .Net DLL but we have not yet worked with a client using this product.

MuleSoft

MuleSoft has a .Net Connector for importing .Net DLLs. RdWebApi could be imported directly, or a custom project can be created which references RdWebApi.

Selenium

Selenium is used purely for web browser automation. Web APIs are a much better approach (particularly if the RdWebApi package is an option).

UI Path

Great news! UI Path is able to import Nuget packages and works with RdWebApi. Using Windows Workflow Foundation Activities, each Web API call can be integrated right into your project. You can use our activities included in the package, or reference RdWebApi from your own .Net project and create your own activities. Please contact us for a detailed explanation on how to set this up inside UI Path.

What is EnergyNode?

This is currently used by Desk Audit clients.

Ever wonder why EnergyLink doesn't just interact directly with your accounting and land systems to transfer data? The main reason is a security one - industry standard practice is to deny all incoming connections unless they match a very specific rule. These rules are tedious to manage as your company and its infrastructure evolve.

EnergyNode solves this problem by running within your company where it can speak directly to your server(s). It acts as a broker between your systems and EnergyLink using an outgoing connection, similar to how your phone handles email and notifications. This enables two-way communication with minimal maintenance.

EnergyNode is...

Secure
  • Uses the same secure communications as your web browser
Easy
  • Installs into any Windows-based system with the .Net Framework installed
  • Can be managed remotely - IT involvement should not be required after initial setup
Flexible
  • Functionality is modular - only install the modules for the systems you use and the functions you need
  • Tasks can be scheduled or triggered manually right from EnergyLink
  • Can run anywhere (e.g. Azure/AWS) if the necessary network access (e.g. VPN) is in place
Always getting better
  • Logging data is sent to EnergyLink for our staff to troubleshoot
  • The service and modules update automatically
  • New modules are easy to add. Contact us today to find out if your system is supported