EIN Filing Workflow

📂

The EIN (Employer Identification Number) filing workflow obtains a federal tax identification number for an existing business entity. EIN filings are not jurisdiction-specific: the order carries only an entity type and the filing type EIN.

How the EIN workflow works

You complete and sign IRS Form SS-4 yourself, then submit it to SingleFile as a PDF together with a short set of structured fields (screening questions, the entity's primary address, a company phone number, a primary contact, an authorization acknowledgement, and the signer's name and title). SingleFile does not build the SS-4 from structured data. Our filing team uses your uploaded SS-4 to obtain the EIN from the IRS, attaches the IRS confirmation letter to the entity as a document, and completes the order. Webhooks tell you when that happens.

The authoritative list of fields is the schema returned by the Schema API (Step 2). Build your payload from that response rather than from this page.

⚠️

Include signer_name (conditional, and not yet in the schema). When the send-for-signature feature is enabled for your account, which is the default, every EIN order must carry a top-level string field named signer_name holding the full name of the person who signed or will sign the SS-4. The rule applies whether or not the SS-4 is already signed; the signer object with an email is only needed when you ask SingleFile to collect the signature. Accounts with the feature switched off are not subject to this requirement. The field is not listed in the schema yet, and until a fix ships a missing signer_name fails the preview and submit calls with an HTTP 500 UNKNOWN_ERROR instead of a 400. Send it alongside signer_name_and_title; it will keep working after the fix.

Prerequisites

  • Valid API access token
  • An existing organization and entity (EIN filings require a formed entity)
  • A completed, signed IRS Form SS-4 as a PDF (or an unsigned one if you want SingleFile to collect the signature, see Step 3)
  • The entity's primary address, a US company telephone number, and a primary contact

All examples use the demo environment, https://api.demo.singlefile.ai. Production is https://api.app.singlefile.io. Demo accepts orders and reports status but does not fulfil them.

Step 1: Find the Entity

EIN filings require an existing formed business entity. List the organization's entities and pick the one you need by name. Keep its id, which is a UUID; numeric dashboard ids are not accepted.

ORGANIZATION_ID="your_organization_uuid"
ACCESS_TOKEN="your_bearer_token_here"

curl -X GET "https://api.demo.singlefile.ai/external-api/v1/organizations/${ORGANIZATION_ID}/entities" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"
import requests

BASE_URL = "https://api.demo.singlefile.ai/external-api/v1"

def get_existing_entity(organization_id, entity_name, access_token):
    response = requests.get(
        f"{BASE_URL}/organizations/{organization_id}/entities",
        headers={"Authorization": f"Bearer {access_token}"},
    )
    response.raise_for_status()
    for entity in response.json()["data"]:
        if entity["name"] == entity_name:
            return entity["id"]
    raise Exception(f"Entity '{entity_name}' not found. EIN filings require a formed business entity.")

ACCESS_TOKEN = "your_bearer_token_here"
entity_id = get_existing_entity("your_organization_uuid", "Acme AI Solutions LLC", ACCESS_TOKEN)
const BASE_URL = "https://api.demo.singlefile.ai/external-api/v1";

async function getExistingEntity(organizationId, entityName, accessToken) {
  const response = await fetch(`${BASE_URL}/organizations/${organizationId}/entities`, {
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  if (!response.ok) throw new Error(`Request failed: ${response.status}`);
  const { data } = await response.json();
  const entity = data.find((e) => e.name === entityName);
  if (entity) return entity.id;
  throw new Error(`Entity '${entityName}' not found. EIN filings require a formed business entity.`);
}

const ACCESS_TOKEN = "your_bearer_token_here";
const entityId = await getExistingEntity("your_organization_uuid", "Acme AI Solutions LLC", ACCESS_TOKEN);
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

const baseURL = "https://api.demo.singlefile.ai/external-api/v1"

func getExistingEntity(organizationID, entityName, accessToken string) (string, error) {
	url := fmt.Sprintf("%s/organizations/%s/entities", baseURL, organizationID)
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("Authorization", "Bearer "+accessToken)

	resp, err := http.DefaultClient.Do(req)
	if err != nil { return "", err }
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("request failed: %d", resp.StatusCode)
	}

	var result struct {
		Data []struct {
			ID   string `json:"id"`
			Name string `json:"name"`
		} `json:"data"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return "", err }

	for _, e := range result.Data {
		if e.Name == entityName { return e.ID, nil }
	}
	return "", fmt.Errorf("entity '%s' not found", entityName)
}

func main() {
	entityID, err := getExistingEntity("your_organization_uuid", "Acme AI Solutions LLC", "your_bearer_token_here")
	if err != nil { panic(err) }
	_ = entityID
}
using System.Net.Http;
using System.Text.Json;

const string BaseUrl = "https://api.demo.singlefile.ai/external-api/v1";

var organizationId = "your_organization_uuid";
var entityName = "Acme AI Solutions LLC";
var accessToken = "your_bearer_token_here";

var entityId = await GetExistingEntity(organizationId, entityName, accessToken);

async Task<string> GetExistingEntity(string orgId, string name, string token)
{
    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
    var response = await client.GetAsync($"{BaseUrl}/organizations/{orgId}/entities");
    if (!response.IsSuccessStatusCode) throw new Exception($"Request failed: {response.StatusCode}");

    var json = await response.Content.ReadAsStringAsync();
    var doc = JsonDocument.Parse(json);
    foreach (var entity in doc.RootElement.GetProperty("data").EnumerateArray())
        if (entity.GetProperty("name").GetString() == name)
            return entity.GetProperty("id").GetString()!;
    throw new Exception($"Entity '{name}' not found.");
}

Step 2: Retrieve the EIN Filing Schema

The schema is the contract for the order payload: it lists every accepted field, which ones are required, and their validation rules. EIN schemas do not take a jurisdiction.

ParameterDescriptionValues
entity_typeType of business entityllc, lp, corporation
filing_typeAlways einein
purposePurpose of EIN applicationnew_business
ENTITY_TYPE="llc"

curl -X GET "https://api.demo.singlefile.ai/external-api/v1/schemas?entity_type=${ENTITY_TYPE}&filing_type=ein&purpose=new_business" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"
def get_ein_schema(entity_type, access_token):
    response = requests.get(
        f"{BASE_URL}/schemas",
        params={"entity_type": entity_type, "filing_type": "ein", "purpose": "new_business"},
        headers={"Authorization": f"Bearer {access_token}"},
    )
    if response.status_code == 404:
        raise Exception(f"EIN schema not found for {entity_type}")
    response.raise_for_status()
    return response.json()["data"]["schema"]

ein_schema = get_ein_schema("llc", ACCESS_TOKEN)
print(ein_schema["required"])
async function getEinSchema(entityType, accessToken) {
  const params = new URLSearchParams({ entity_type: entityType, filing_type: "ein", purpose: "new_business" });
  const res = await fetch(`${BASE_URL}/schemas?${params}`, {
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  if (res.status === 404) throw new Error(`EIN schema not found for ${entityType}`);
  if (!res.ok) throw new Error(`Failed to retrieve EIN schema: ${res.status}`);
  return (await res.json()).data.schema;
}

const einSchema = await getEinSchema("llc", ACCESS_TOKEN);
console.log(einSchema.required);
func getEinSchema(entityType, accessToken string) (map[string]interface{}, error) {
	url := fmt.Sprintf("%s/schemas?entity_type=%s&filing_type=ein&purpose=new_business", baseURL, entityType)
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("Authorization", "Bearer "+accessToken)
	resp, err := http.DefaultClient.Do(req)
	if err != nil { return nil, err }
	defer resp.Body.Close()
	if resp.StatusCode == 404 {
		return nil, fmt.Errorf("EIN schema not found for %s", entityType)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to retrieve EIN schema: %d", resp.StatusCode)
	}
	var result struct {
		Data struct {
			Schema map[string]interface{} `json:"schema"`
		} `json:"data"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, err }
	return result.Data.Schema, nil
}
async Task<JsonElement> GetEinSchema(string entityType, string token)
{
    var url = $"{BaseUrl}/schemas?entity_type={entityType}&filing_type=ein&purpose=new_business";
    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
    var res = await client.GetAsync(url);
    if (res.StatusCode == System.Net.HttpStatusCode.NotFound)
        throw new Exception($"EIN schema not found for {entityType}");
    res.EnsureSuccessStatusCode();
    var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
    return doc.RootElement.GetProperty("data").GetProperty("schema").Clone();
}

Step 3: Build the EIN Order Payload

The payload is the signed SS-4 plus the structured fields below. Field names and requirements come from the schema in Step 2; the table summarizes the LLC schema at the time of writing.

FieldDescriptionRequired
filing_typeAlways "EIN"Yes
entity_typellc, lp or corporationYes
entity{ "id": "<entity uuid>" } from Step 1Yes
variantService level. Use "standard"Yes
documentThe completed SS-4 PDF: { "filename", "content_type", "content" } with content base64-encodedYes
ss4_signature_status"signed" if the uploaded SS-4 is signed, "unsigned_send_for_signature" to have SingleFile collect the signatureYes
signer{ "first_name", "last_name", "email" } of the person to sign. Required when ss4_signature_status is "unsigned_send_for_signature"Conditional
signer_name_and_title{ "first_name", "last_name", "title", "phone" } of the person signing the applicationRecommended
signer_nameFull name of the signer as a plain string. Required whenever send-for-signature is enabled for your account (the default), even for an already-signed SS-4 (see the callout above)Conditional, currently yes
needs_form_720Does the business need to file Form 720 (Quarterly Federal Excise Tax Return)? "yes" or "no"Yes
owns_highway_motor_vehicleDoes it own a highway motor vehicle with taxable gross weight of 55,000 lb or more? "yes" or "no"Yes
expects_w2_employeesWill it have employees receiving Forms W-2 in the next 12 months? "yes" or "no"Yes
involves_gamblingDoes it involve gambling or wagering? "yes" or "no"Yes
sells_alcohol_tobacco_firearmsDoes it sell or manufacture alcohol, tobacco or firearms? "yes" or "no"Yes
primary_addressPhysical address: street_address, city, state (2-letter), postal_code, country ("US")Yes
mailing_addressSame shape as primary_address, if differentNo
company_telephoneUS phone number, required by the IRSYes
primary_contact{ "first_name", "last_name", "email" }, optional telephone_number (10 digits)Yes
authorization_acktrue: the entity authorizes SingleFile to apply for and receive the EIN on its behalfYes
commentsFree-text note for the filing teamNo

A complete payload:

{
  "filing_type": "EIN",
  "entity_type": "llc",
  "entity": { "id": "e0190d40-50cd-4601-8517-3693725122fd" },
  "variant": "standard",
  "document": {
    "filename": "ss4.pdf",
    "content_type": "application/pdf",
    "content": "JVBERi0xLjQK...base64 of the signed SS-4..."
  },
  "ss4_signature_status": "signed",
  "signer_name_and_title": {
    "first_name": "Jane",
    "last_name": "Smith",
    "title": "Managing Member",
    "phone": "3025550123"
  },
  "signer_name": "Jane Smith",
  "needs_form_720": "no",
  "owns_highway_motor_vehicle": "no",
  "expects_w2_employees": "yes",
  "involves_gambling": "no",
  "sells_alcohol_tobacco_firearms": "no",
  "primary_address": {
    "street_address": "123 Innovation Drive, Suite 100",
    "city": "Wilmington",
    "state": "DE",
    "postal_code": "19801",
    "country": "US"
  },
  "company_telephone": "3025550123",
  "primary_contact": {
    "first_name": "Jane",
    "last_name": "Smith",
    "email": "[email protected]"
  },
  "authorization_ack": true
}
# Save the JSON above as ein_payload.json, replacing document.content with your
# base64-encoded SS-4, for example:
#   base64 -i ss4.pdf | tr -d '\n'
# Steps 4 and 5 send it with -d @ein_payload.json
import base64

def create_ein_payload(entity_id, entity_type, ss4_pdf_path):
    with open(ss4_pdf_path, "rb") as f:
        ss4_base64 = base64.b64encode(f.read()).decode("ascii")

    return {
        "filing_type": "EIN",
        "entity_type": entity_type,
        "entity": {"id": entity_id},
        "variant": "standard",
        "document": {
            "filename": "ss4.pdf",
            "content_type": "application/pdf",
            "content": ss4_base64,
        },
        "ss4_signature_status": "signed",
        "signer_name_and_title": {
            "first_name": "Jane",
            "last_name": "Smith",
            "title": "Managing Member",
            "phone": "3025550123",
        },
        "signer_name": "Jane Smith",
        "needs_form_720": "no",
        "owns_highway_motor_vehicle": "no",
        "expects_w2_employees": "yes",
        "involves_gambling": "no",
        "sells_alcohol_tobacco_firearms": "no",
        "primary_address": {
            "street_address": "123 Innovation Drive, Suite 100",
            "city": "Wilmington",
            "state": "DE",
            "postal_code": "19801",
            "country": "US",
        },
        "company_telephone": "3025550123",
        "primary_contact": {
            "first_name": "Jane",
            "last_name": "Smith",
            "email": "[email protected]",
        },
        "authorization_ack": True,
    }

ein_payload = create_ein_payload(entity_id, "llc", "ss4.pdf")
import { readFile } from "node:fs/promises";

async function createEinPayload(entityId, entityType, ss4PdfPath) {
  const ss4Base64 = (await readFile(ss4PdfPath)).toString("base64");

  return {
    filing_type: "EIN",
    entity_type: entityType,
    entity: { id: entityId },
    variant: "standard",
    document: {
      filename: "ss4.pdf",
      content_type: "application/pdf",
      content: ss4Base64,
    },
    ss4_signature_status: "signed",
    signer_name_and_title: {
      first_name: "Jane",
      last_name: "Smith",
      title: "Managing Member",
      phone: "3025550123",
    },
    signer_name: "Jane Smith",
    needs_form_720: "no",
    owns_highway_motor_vehicle: "no",
    expects_w2_employees: "yes",
    involves_gambling: "no",
    sells_alcohol_tobacco_firearms: "no",
    primary_address: {
      street_address: "123 Innovation Drive, Suite 100",
      city: "Wilmington",
      state: "DE",
      postal_code: "19801",
      country: "US",
    },
    company_telephone: "3025550123",
    primary_contact: {
      first_name: "Jane",
      last_name: "Smith",
      email: "[email protected]",
    },
    authorization_ack: true,
  };
}

const einPayload = await createEinPayload(entityId, "llc", "ss4.pdf");
import (
	"encoding/base64"
	"os"
)

func createEinPayload(entityID, entityType, ss4PdfPath string) (map[string]interface{}, error) {
	pdf, err := os.ReadFile(ss4PdfPath)
	if err != nil { return nil, err }

	return map[string]interface{}{
		"filing_type": "EIN",
		"entity_type": entityType,
		"entity":      map[string]string{"id": entityID},
		"variant":     "standard",
		"document": map[string]string{
			"filename":     "ss4.pdf",
			"content_type": "application/pdf",
			"content":      base64.StdEncoding.EncodeToString(pdf),
		},
		"ss4_signature_status": "signed",
		"signer_name_and_title": map[string]string{
			"first_name": "Jane", "last_name": "Smith", "title": "Managing Member", "phone": "3025550123",
		},
		"signer_name":                    "Jane Smith",
		"needs_form_720":                 "no",
		"owns_highway_motor_vehicle":     "no",
		"expects_w2_employees":           "yes",
		"involves_gambling":              "no",
		"sells_alcohol_tobacco_firearms": "no",
		"primary_address": map[string]string{
			"street_address": "123 Innovation Drive, Suite 100",
			"city": "Wilmington", "state": "DE", "postal_code": "19801", "country": "US",
		},
		"company_telephone": "3025550123",
		"primary_contact": map[string]string{
			"first_name": "Jane", "last_name": "Smith", "email": "[email protected]",
		},
		"authorization_ack": true,
	}, nil
}
Dictionary<string, object> CreateEinPayload(string entityId, string entityType, string ss4PdfPath)
{
    var ss4Base64 = Convert.ToBase64String(File.ReadAllBytes(ss4PdfPath));

    return new Dictionary<string, object> {
        ["filing_type"] = "EIN",
        ["entity_type"] = entityType,
        ["entity"] = new Dictionary<string, string> { ["id"] = entityId },
        ["variant"] = "standard",
        ["document"] = new Dictionary<string, string> {
            ["filename"] = "ss4.pdf", ["content_type"] = "application/pdf", ["content"] = ss4Base64
        },
        ["ss4_signature_status"] = "signed",
        ["signer_name_and_title"] = new Dictionary<string, string> {
            ["first_name"] = "Jane", ["last_name"] = "Smith", ["title"] = "Managing Member", ["phone"] = "3025550123"
        },
        ["signer_name"] = "Jane Smith",
        ["needs_form_720"] = "no",
        ["owns_highway_motor_vehicle"] = "no",
        ["expects_w2_employees"] = "yes",
        ["involves_gambling"] = "no",
        ["sells_alcohol_tobacco_firearms"] = "no",
        ["primary_address"] = new Dictionary<string, string> {
            ["street_address"] = "123 Innovation Drive, Suite 100", ["city"] = "Wilmington",
            ["state"] = "DE", ["postal_code"] = "19801", ["country"] = "US"
        },
        ["company_telephone"] = "3025550123",
        ["primary_contact"] = new Dictionary<string, string> {
            ["first_name"] = "Jane", ["last_name"] = "Smith", ["email"] = "[email protected]"
        },
        ["authorization_ack"] = true
    };
}

Having SingleFile collect the signature

If the SS-4 is not yet signed, set ss4_signature_status to "unsigned_send_for_signature" and add a signer object with the signer's first_name, last_name and email. SingleFile sends the signature request after the order is placed; you receive signature.requested, then signature.completed, signature.declined or signature.expired webhooks as it progresses. The filing does not start until the signed SS-4 is returned.

Step 4: Preview the Order

Preview validates the payload and returns the priced line items without creating an order or storing the document. A 200 response means the payload is accepted; a 400 lists the failing fields.

curl -X POST "https://api.demo.singlefile.ai/external-api/v1/orders?preview=true" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d @ein_payload.json
def preview_ein_application(payload, access_token):
    response = requests.post(
        f"{BASE_URL}/orders",
        json=payload,
        params={"preview": "true"},
        headers={"Authorization": f"Bearer {access_token}"},
    )
    if response.status_code == 200:
        data = response.json()["data"]
        print("EIN preview accepted. Total:", data["total_amount"])
        return data
    error = response.json().get("error", {})
    print("Preview failed:", error.get("message", "Unknown error"), error.get("errors", ""))
    return None

if not preview_ein_application(ein_payload, ACCESS_TOKEN):
    raise Exception("EIN filing preview failed")
async function previewEinApplication(payload, accessToken) {
  const res = await fetch(`${BASE_URL}/orders?preview=true`, {
    method: "POST",
    headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" },
    body: JSON.stringify(payload),
  });
  if (res.ok) {
    const { data } = await res.json();
    console.log("EIN preview accepted. Total:", data.total_amount);
    return data;
  }
  const err = await res.json().catch(() => ({}));
  console.log("Preview failed:", err?.error?.message ?? "Unknown error", err?.error?.errors ?? "");
  return null;
}

if (!(await previewEinApplication(einPayload, ACCESS_TOKEN))) throw new Error("EIN filing preview failed");
func previewEinApplication(payload map[string]interface{}, accessToken string) bool {
	body, _ := json.Marshal(payload)
	req, _ := http.NewRequest("POST", baseURL+"/orders?preview=true", bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer "+accessToken)
	req.Header.Set("Content-Type", "application/json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil { return false }
	defer resp.Body.Close()
	if resp.StatusCode == http.StatusOK {
		fmt.Println("EIN preview accepted")
		return true
	}
	var errResp struct { Error struct { Message string `json:"message"` } `json:"error"` }
	json.NewDecoder(resp.Body).Decode(&errResp)
	fmt.Println("Preview failed:", errResp.Error.Message)
	return false
}
async Task<bool> PreviewEinApplication(object payload, string token)
{
    var json = JsonSerializer.Serialize(payload);
    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
    var content = new StringContent(json, Encoding.UTF8, "application/json");
    var res = await client.PostAsync($"{BaseUrl}/orders?preview=true", content);
    if (res.IsSuccessStatusCode) { Console.WriteLine("EIN preview accepted"); return true; }
    var err = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
    var msg = err.RootElement.TryGetProperty("error", out var e) ? e.GetProperty("message").GetString() : "Unknown error";
    Console.WriteLine($"Preview failed: {msg}");
    return false;
}

Step 5: Submit the Order

Submit the same payload without the preview parameter. A 201 response returns the order, including its order_id. The SS-4 is stored as a document on the entity at this point.

curl -X POST "https://api.demo.singlefile.ai/external-api/v1/orders" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d @ein_payload.json
def submit_ein_application(payload, access_token):
    response = requests.post(
        f"{BASE_URL}/orders",
        json=payload,
        headers={"Authorization": f"Bearer {access_token}"},
    )
    if response.status_code == 201:
        order = response.json()["data"]
        print(f"EIN filing submitted! Order ID: {order['order_id']}")
        return order
    print("Submission failed:", response.json().get("error", {}).get("message", "Unknown error"))
    return None

submitted_order = submit_ein_application(ein_payload, ACCESS_TOKEN)
async function submitEinApplication(payload, accessToken) {
  const res = await fetch(`${BASE_URL}/orders`, {
    method: "POST",
    headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" },
    body: JSON.stringify(payload),
  });
  if (res.status === 201) {
    const { data } = await res.json();
    console.log("EIN filing submitted! Order ID:", data.order_id);
    return data;
  }
  const err = await res.json().catch(() => ({}));
  console.log("Submission failed:", err?.error?.message ?? "Unknown error");
  return null;
}
func submitEinApplication(payload map[string]interface{}, accessToken string) map[string]interface{} {
	body, _ := json.Marshal(payload)
	req, _ := http.NewRequest("POST", baseURL+"/orders", bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer "+accessToken)
	req.Header.Set("Content-Type", "application/json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil { return nil }
	defer resp.Body.Close()
	if resp.StatusCode == 201 {
		var result struct { Data map[string]interface{} `json:"data"` }
		json.NewDecoder(resp.Body).Decode(&result)
		fmt.Println("EIN filing submitted! Order ID:", result.Data["order_id"])
		return result.Data
	}
	var errResp struct { Error struct { Message string `json:"message"` } `json:"error"` }
	json.NewDecoder(resp.Body).Decode(&errResp)
	fmt.Println("Submission failed:", errResp.Error.Message)
	return nil
}
async Task<JsonElement?> SubmitEinApplication(object payload, string token)
{
    var json = JsonSerializer.Serialize(payload);
    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
    var content = new StringContent(json, Encoding.UTF8, "application/json");
    var res = await client.PostAsync($"{BaseUrl}/orders", content);
    if (res.StatusCode == System.Net.HttpStatusCode.Created)
    {
        var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
        var data = doc.RootElement.GetProperty("data");
        Console.WriteLine($"EIN filing submitted! Order ID: {data.GetProperty("order_id").GetString()}");
        return data;
    }
    var errDoc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
    var errMsg = errDoc.RootElement.TryGetProperty("error", out var errE) ? errE.GetProperty("message").GetString() : "Unknown error";
    Console.WriteLine($"Submission failed: {errMsg}");
    return null;
}

Step 6: Receive the EIN

Fulfilment is handled by SingleFile's filing team. Once the EIN is issued, the IRS confirmation letter is attached to the entity as a document and the order is completed. Register a webhook endpoint to be notified:

EventMeaning
order.submittedYour order was received
document.createdA document was added to the entity. For EIN orders this is the IRS EIN letter, with document_type EIN
order.completedThe EIN order is finished
order.cancelledThe order was cancelled

The EIN number itself is on the IRS letter. To fetch it, list the entity's documents and download the one with document_type EIN:

# List documents on the entity
curl -X GET "https://api.demo.singlefile.ai/external-api/v1/entities/${ENTITY_ID}/documents" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"

# Fetch one document; the response includes a document_url to download the file
curl -X GET "https://api.demo.singlefile.ai/external-api/v1/documents/${DOCUMENT_ID}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"
def get_ein_letter(entity_id, access_token):
    headers = {"Authorization": f"Bearer {access_token}"}
    documents = requests.get(f"{BASE_URL}/entities/{entity_id}/documents", headers=headers).json()["data"]
    ein_docs = [d for d in documents if d["document_type"] == "EIN"]
    if not ein_docs:
        return None
    detail = requests.get(f"{BASE_URL}/documents/{ein_docs[0]['id']}", headers=headers).json()["data"]
    return detail["document_url"]
async function getEinLetter(entityId, accessToken) {
  const headers = { Authorization: `Bearer ${accessToken}` };
  const { data: documents } = await (await fetch(`${BASE_URL}/entities/${entityId}/documents`, { headers })).json();
  const einDoc = documents.find((d) => d.document_type === "EIN");
  if (!einDoc) return null;
  const { data: detail } = await (await fetch(`${BASE_URL}/documents/${einDoc.id}`, { headers })).json();
  return detail.document_url;
}

You can also poll GET /external-api/v1/orders/{order_id}; its status moves through received, submitted and completed, and status_history records each transition with a timestamp.

Complete EIN Workflow

def complete_ein_workflow(organization_id, entity_name, entity_type, ss4_pdf_path, access_token):
    entity_id = get_existing_entity(organization_id, entity_name, access_token)
    ein_schema = get_ein_schema(entity_type, access_token)  # inspect "required" if the contract changes
    ein_payload = create_ein_payload(entity_id, entity_type, ss4_pdf_path)

    if not preview_ein_application(ein_payload, access_token):
        raise Exception("EIN filing preview failed")

    submitted_order = submit_ein_application(ein_payload, access_token)
    if not submitted_order:
        raise Exception("EIN filing submission failed")

    return {"entity_id": entity_id, "order_id": submitted_order["order_id"]}

result = complete_ein_workflow(
    organization_id="your_organization_uuid",
    entity_name="Acme AI Solutions LLC",
    entity_type="llc",
    ss4_pdf_path="ss4.pdf",
    access_token=ACCESS_TOKEN,
)
async function completeEinWorkflow(organizationId, entityName, entityType, ss4PdfPath, accessToken) {
  const entityId = await getExistingEntity(organizationId, entityName, accessToken);
  await getEinSchema(entityType, accessToken); // inspect "required" if the contract changes
  const einPayload = await createEinPayload(entityId, entityType, ss4PdfPath);
  if (!(await previewEinApplication(einPayload, accessToken))) throw new Error("EIN filing preview failed");
  const order = await submitEinApplication(einPayload, accessToken);
  if (!order) throw new Error("EIN filing submission failed");
  return { entity_id: entityId, order_id: order.order_id };
}

Common Errors

ResponseCauseFix
400 SCHEMA_VALIDATION_ERROR with "<field> is required"A required field from the schema is missingAdd the field; the errors array names each one
400 on entity with "You must provide one of: entity_type or id or jurisdiction or name or organization"entity.id is not a UUID string (for example a numeric id), or the object mixes id with other fieldsSend { "id": "<uuid>" } only
500 UNKNOWN_ERROR on preview or submitsigner_name is missing (see the callout at the top)Add the top-level signer_name string

Did this page help you?