Send invoices, purchase orders, and contracts between businesses with cryptographic sender identity and permit-based authorization — no EDI VANs required.
Traditional B2B document exchange (EDI, AS2, SFTP) solves the wrong problem: it focuses on transport security (encrypting the pipe) but ignores sender identity. Anyone with SFTP credentials can send any file. TFEP solves this:
- Invoices are signed by
did:web:vendor.com— unforgeable, auditable - Permit system authorizes specific vendors to send invoices to your gateway
- Delivery receipts confirm the document reached the buyer's gateway
- Audit trail in SQLite: every document stored with full TFEP envelope
| TFEP Type | Use case |
|---|---|
invoice |
Accounts receivable — vendor → buyer |
purchase_order |
Procurement — buyer → vendor |
contract |
Legal agreements — any direction |
receipt |
Delivery acknowledgment (auto-generated) |
curl -X POST http://localhost:8080/api/v1/documents/send \
-H "Authorization: Bearer $TFEP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipient_did": "did:web:buyer.example",
"type": "invoice",
"subject": "Invoice INV-2026-042 — $12,500 USD",
"body": "Dear Buyer,\n\nPlease find attached invoice INV-2026-042...",
"metadata": {
"invoice_number": "INV-2026-042",
"issue_date": "2026-05-31",
"due_date": "2026-06-30",
"amount": 12500.00,
"currency": "USD",
"vendor_id": "ACME-001",
"po_reference": "PO-2026-009",
"line_items": [
{ "description": "Software licenses Q3", "quantity": 5, "unit_price": 2500.00, "total": 12500.00 }
]
}
}'Response:
{"status": "sent", "message_id": "550e8400-e29b-41d4-a716-446655440000"}The document is:
- Wrapped in a TFEP envelope with
type: invoice,sender_did: did:web:your-gateway.com - Signed with your gateway's Ed25519 private key
- Delivered to the buyer's SMTP/TFEP gateway (MX lookup)
- Stored locally as
direction: outbound
./tfep-gateway send \
--to accounts@buyer.example \
--type invoice \
--subject "Invoice INV-2026-042" \
--body "Payment due 30 days from invoice date."import (
"github.com/tfep/tfep-gateway/internal/smtp"
"github.com/tfep/tfep-gateway/pkg/tfep"
"encoding/json"
)
meta, _ := json.Marshal(map[string]any{
"invoice_number": "INV-2026-042",
"amount": 12500.00,
"currency": "USD",
})
env := tfep.Envelope{
Version: "1",
MessageID: uuid.New().String(),
MessageType: tfep.TypeInvoice,
SenderDID: "did:web:vendor.example",
RecipientDID: "did:web:buyer.example",
Timestamp: time.Now().UTC(),
Subject: "Invoice INV-2026-042",
Metadata: meta,
}
smtp.Send(cfg, smtp.SendOptions{Envelope: env, Body: body, SigningKey: priv})When a buyer's gateway receives an invoice via SMTP:
- The SMTP server extracts the TFEP envelope from the MIME parts
- Resolves the sender's DID and verifies the Ed25519 signature
- Assigns a trust tier (Verified if signature passes)
- Stores the message with
direction: inbound,message_type: invoice - Available at
GET /api/v1/documents?direction=inbound&type=invoice
# All incoming invoices
curl -H "Authorization: Bearer $TFEP_API_KEY" \
"http://localhost:8080/api/v1/documents?direction=inbound&type=invoice"
# Outbound purchase orders
curl -H "Authorization: Bearer $TFEP_API_KEY" \
"http://localhost:8080/api/v1/documents?direction=outbound&type=purchase_order"
# Get a specific document with its metadata
curl -H "Authorization: Bearer $TFEP_API_KEY" \
"http://localhost:8080/api/v1/documents/{message-id}"Before a vendor can send you invoices (or any message type requiring authorization), issue them a permit:
# Issue a permit to vendor did:web:acme.com, valid for 1 year
curl -X POST http://localhost:8080/api/v1/permits \
-H "Authorization: Bearer $TFEP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"grantee_did": "did:web:acme.com",
"expiry_hours": 8760
}'The vendor includes the permit_token in their invoice envelope. Your gateway validates it before accepting the invoice. Invoices without a valid permit are held in the Unknown queue.
Permit lifecycle:
- Issue → vendor can send (any
invoicetype message) - Revoke → vendor cannot send (immediate effect)
- Expire → vendor cannot send (automatic after
expiry_hours)
The metadata field accepts any JSON. Recommended fields for invoices:
{
"invoice_number": "INV-2026-042",
"issue_date": "2026-05-31",
"due_date": "2026-06-30",
"amount": 12500.00,
"currency": "USD",
"vendor_id": "ACME-001",
"po_reference": "PO-2026-009",
"payment_terms": "Net 30",
"bank_account": "...",
"line_items": [...]
}For purchase orders:
{
"po_number": "PO-2026-009",
"issue_date": "2026-05-28",
"delivery_date": "2026-06-15",
"ship_to": "123 Main St, Springfield",
"line_items": [...]
}Every document is stored in SQLite:
SELECT id, message_type, sender_did, recipient_did, subject,
document_meta, direction, received_at
FROM messages
WHERE message_type IN ('invoice', 'purchase_order', 'contract')
ORDER BY received_at DESC;The full TFEP envelope (with Ed25519 signature) is stored in tfep_envelope — cryptographic proof that the document came from the claimed sender, at the claimed time, with unmodified content.
# Send an invoice using the example program
go run ./examples/b2b-invoicing \
--gateway http://localhost:8080 \
--api-key $TFEP_API_KEY \
--to did:web:buyer.example \
--invoice-number INV-2026-001 \
--amount 4999 \
--currency USD| Feature | EDI (AS2/SFTP) | TFEP |
|---|---|---|
| Sender identity | Directory configuration | Cryptographic (Ed25519) |
| Forgeability | Anyone with credentials | Computationally infeasible |
| Infrastructure | VAN / AS2 server | Any SMTP gateway |
| Onboarding | Days (manual config) | Minutes (keygen + DNS TXT) |
| Format | X12 / EDIFACT | JSON + MIME |
| Audit trail | VAN logs (external) | Local SQLite (you own it) |
| Cost | $500–$5000/month VAN fee | Free (self-hosted) |