Skip to content

Accounting

The Accounting section of the Entity API covers GL journal entries, currency exchange rates, and customer form template assignments. These endpoints are used for financial integrations, multi-currency operations, and document configuration.


GL Entries

Endpoints

Method URL Description
GET /entity/accounting/gl/ Get all GL entries (ArrayOfGLEntry)
POST /entity/accounting/gl/ Create a new GL entry
GET /entity/accounting/gl/{companyNo}_{glUid} Get a specific GL entry
PUT /entity/accounting/gl/{companyNo}_{glUid} Update a GL entry
GET /entity/accounting/gl/new Get a blank GL entry with defaults
GET /entity/accounting/gl/ping Health check

GL Entry Fields

Field Type Description
company_no string Company identifier
account_number string GL account number (chart of accounts)
period int Accounting period (1–12 for calendar year)
year_for_period int Fiscal year the period belongs to
journal_id string Journal identifier (e.g., AP, AR, GJ)
amount decimal Transaction amount in functional currency
source string Source module or system (e.g., AP, AR, OE)
description string Human-readable transaction description
currency_id string Currency code for the transaction
foreign_amount decimal Amount in foreign currency (if multi-currency)
transaction_date date Date of the transaction
transaction_number string Reference or document number
approved bool Whether the entry has been approved
job_id string Job or project ID for job-costing entries
gl_uid int System-assigned unique identifier

Functional vs. foreign currency

amount is always in the company's functional currency. foreign_amount is populated for multi-currency transactions and stores the value in the originating currency. currency_id identifies which currency foreign_amount is denominated in.

XML Example — GL Entry

<GLEntry xmlns="http://www.epicor.com/entity">
  <company_no>01</company_no>
  <gl_uid>98432</gl_uid>
  <account_number>4000-00</account_number>
  <period>11</period>
  <year_for_period>2025</year_for_period>
  <journal_id>AR</journal_id>
  <amount>1250.00</amount>
  <source>AR</source>
  <description>Invoice 10043 - Acme Corp</description>
  <currency_id>USD</currency_id>
  <foreign_amount>0.00</foreign_amount>
  <transaction_date>2025-11-15</transaction_date>
  <transaction_number>INV-10043</transaction_number>
  <approved>true</approved>
  <job_id></job_id>
</GLEntry>

XML Example — Create GL Entry (Request Body)

<GLEntry xmlns="http://www.epicor.com/entity">
  <company_no>01</company_no>
  <account_number>6100-00</account_number>
  <period>11</period>
  <year_for_period>2025</year_for_period>
  <journal_id>GJ</journal_id>
  <amount>500.00</amount>
  <source>GJ</source>
  <description>Manual adjustment - Q4 accrual</description>
  <currency_id>USD</currency_id>
  <foreign_amount>0.00</foreign_amount>
  <transaction_date>2025-11-30</transaction_date>
  <transaction_number>ADJ-2025-1130</transaction_number>
  <approved>false</approved>
</GLEntry>

Do not set gl_uid on creation

gl_uid is system-assigned. Do not include it in POST payloads. The response will contain the assigned value.

C# Example — Create GL Entry

public async Task<GLEntry> CreateGLEntryAsync(GLEntryCreateRequest request)
{
    // Step 1: Fetch template
    var templateResponse = await _httpClient.GetAsync($"{_baseUrl}/entity/accounting/gl/new");
    templateResponse.EnsureSuccessStatusCode();
    var templateXml = await templateResponse.Content.ReadAsStringAsync();

    var serializer = new XmlSerializer(typeof(GLEntry));
    using var templateReader = new StringReader(templateXml);
    var template = (GLEntry)serializer.Deserialize(templateReader);

    // Step 2: Populate fields
    template.company_no         = request.CompanyNo;
    template.account_number     = request.AccountNumber;
    template.period             = request.Period;
    template.year_for_period    = request.Year;
    template.journal_id         = request.JournalId;
    template.amount             = request.Amount;
    template.source             = request.Source;
    template.description        = request.Description;
    template.currency_id        = request.CurrencyId;
    template.transaction_date   = request.TransactionDate;
    template.transaction_number = request.TransactionNumber;
    template.approved           = false;

    // Step 3: Serialize and POST
    using var writer = new StringWriter();
    serializer.Serialize(writer, template);
    var xml = writer.ToString();

    var content = new StringContent(xml, Encoding.UTF8, "application/xml");
    var response = await _httpClient.PostAsync($"{_baseUrl}/entity/accounting/gl/", content);
    response.EnsureSuccessStatusCode();

    var responseXml = await response.Content.ReadAsStringAsync();
    using var reader = new StringReader(responseXml);
    return (GLEntry)serializer.Deserialize(reader);
}

Exchange Rates

Endpoints

Method URL Description
GET /entity/accounting/exchangerates/ Get all exchange rates
POST /entity/accounting/exchangerates/ Create a new exchange rate
GET /entity/accounting/exchangerates/{companyId}_{rateId} Get a specific rate
PUT /entity/accounting/exchangerates/{companyId}_{rateId} Update an exchange rate
GET /entity/accounting/exchangerates/new Get a blank rate with defaults
GET /entity/accounting/exchangerates/ping Health check

Exchange Rate Fields

Field Type Description
currency_id string Currency code (e.g., EUR, CAD, GBP)
exchange_rate decimal Rate relative to functional currency (e.g., 1.085 for EUR/USD)
effective_date date Date from which this rate is effective
rate_type string Rate category (e.g., SPOT, BUDGET, AVERAGE)
company_id string Company identifier

Effective date and rate history

P21 maintains exchange rate history. New rates do not overwrite old ones — each rate record is keyed by currency, rate type, and effective date. Create a new record for each rate change rather than updating existing records. This preserves audit history for period-end financial reporting.

XML Example — Exchange Rate

<ExchangeRate xmlns="http://www.epicor.com/entity">
  <company_id>01</company_id>
  <currency_id>EUR</currency_id>
  <exchange_rate>1.0850</exchange_rate>
  <effective_date>2025-11-01</effective_date>
  <rate_type>SPOT</rate_type>
</ExchangeRate>

XML Example — Create Exchange Rate (Request Body)

<ExchangeRate xmlns="http://www.epicor.com/entity">
  <company_id>01</company_id>
  <currency_id>CAD</currency_id>
  <exchange_rate>0.7320</exchange_rate>
  <effective_date>2025-12-01</effective_date>
  <rate_type>SPOT</rate_type>
</ExchangeRate>

C# Example — Create Exchange Rate

public async Task CreateExchangeRateAsync(string companyId, string currencyId,
    decimal rate, DateTime effectiveDate, string rateType = "SPOT")
{
    // Fetch template
    var templateResponse = await _httpClient
        .GetAsync($"{_baseUrl}/entity/accounting/exchangerates/new");
    templateResponse.EnsureSuccessStatusCode();
    var templateXml = await templateResponse.Content.ReadAsStringAsync();

    var serializer = new XmlSerializer(typeof(ExchangeRate));
    using var templateReader = new StringReader(templateXml);
    var template = (ExchangeRate)serializer.Deserialize(templateReader);

    template.company_id      = companyId;
    template.currency_id     = currencyId;
    template.exchange_rate   = rate;
    template.effective_date  = effectiveDate;
    template.rate_type       = rateType;

    using var writer = new StringWriter();
    serializer.Serialize(writer, template);
    var xml = writer.ToString();

    var content = new StringContent(xml, Encoding.UTF8, "application/xml");
    var response = await _httpClient
        .PostAsync($"{_baseUrl}/entity/accounting/exchangerates/", content);
    response.EnsureSuccessStatusCode();
}

Customer Form Templates

Endpoints

Method URL Description
GET /entity/accounting/customerformtemplates/ Get all customer form template assignments
POST /entity/accounting/customerformtemplates/ Create a new template assignment
GET /entity/accounting/customerformtemplates/{companyId}_{templateId} Get a specific assignment
PUT /entity/accounting/customerformtemplates/{companyId}_{templateId} Update a template assignment
GET /entity/accounting/customerformtemplates/new Get a blank template assignment
GET /entity/accounting/customerformtemplates/ping Health check

Customer form templates control which document layout (invoice format, statement format, etc.) is used for a given customer or customer group. This endpoint allows integration code to read and modify those assignments programmatically.

Use cases

Customer form template assignments are typically modified when onboarding new customers with custom document requirements, or when migrating document templates across environments. Most integrations do not need to modify these unless they are managing customer setup workflows end-to-end.


Notes and Common Pitfalls

GL entries and period locking

P21 supports period locking to prevent changes to closed accounting periods. Attempting to POST a GL entry into a locked period will return a validation error. Always verify the target period and year_for_period are open before creating entries.

Journal IDs and source codes

Use recognized journal IDs and source codes for your GL entries. Unrecognized values may pass validation but will cause GL entries to appear incorrectly in reports or be excluded from journal-specific queries. Consult your P21 chart of accounts configuration for valid values.

Approved flag on GL entries

The approved field controls whether a GL entry is included in period-end reporting. Entries created with approved=false are visible in the system but excluded from financial statements until approved. Use this for entries that require review before posting.