This playbook covers integration between ERP systems (SAP S/4HANA, Oracle ERP Cloud, Dynamics 365) and the three dominant SRM/procurement platforms: SAP Ariba, Coupa, and Jaggaer ONE. It addresses the full supplier interaction lifecycle from punch-out catalog browsing through PO transmission, order confirmation, advance ship notice (ASN), invoice flip, and 3-way match. GEP SMART follows similar cXML patterns but is not covered in detail.
This card does NOT cover internal procurement workflows (see procure-to-pay-integration) or iPaaS platform selection (see ipaas-platform-comparison). It focuses on the external supplier portal integration layer — the protocols, data flows, and failure patterns specific to SRM-to-ERP connectivity.
| System | Role | API Surface | Direction |
|---|---|---|---|
| SAP Ariba | SRM portal — punch-out, sourcing, supplier management | cXML, OCI, REST, SOAP | Bidirectional |
| Coupa | BSM platform — procurement, invoicing, expenses | REST API, cXML, CSV/SFTP | Bidirectional |
| Jaggaer ONE | Source-to-pay — catalogs, contracts, supplier mgmt | REST, cXML, OCI, SFTP | Bidirectional |
| SAP S/4HANA | ERP — financial master, PO system of record | OData v4, BAPI/RFC, IDoc | Inbound/Outbound |
| Oracle ERP Cloud | ERP — procurement + AP module | REST, SOAP, FBDI | Inbound/Outbound |
| Middleware (CIG/iPaaS) | Protocol translation, mapping, orchestration | N/A | Orchestrator |
| API Surface | Protocol | Platform | Best For | Real-time? | Bulk? | Bidirectional? |
|---|---|---|---|---|---|---|
| cXML | HTTPS/XML | Ariba, Coupa, Jaggaer | PO, Invoice, ASN, Punch-out | Yes | No | Yes |
| OCI 5.0 | HTTP POST/GET (JSON in 5.0) | SAP Ariba (SAP ERPs) | Punch-out catalog only | Yes | No | One-way (cart return) |
| Ariba REST API | HTTPS/JSON | SAP Ariba | Supplier management, sourcing events | Yes | Limited | Yes |
| Ariba SOAP/Web Services | HTTPS/XML | SAP Ariba | Document export, reporting | Yes | Yes (batched) | Outbound only |
| Coupa REST API | HTTPS/JSON | Coupa | CRUD on all objects (suppliers, POs, invoices) | Yes | Yes (paging) | Yes |
| Coupa CSV/SFTP | SFTP/CSV | Coupa | Bulk supplier import, catalog loads | No | Yes | Inbound only |
| Jaggaer REST API | HTTPS/JSON | Jaggaer ONE | Custom integrations, supplier data | Yes | Limited | Yes |
| CIG (SAP Integration Suite) | IDoc/Proxy to cXML | SAP Ariba + SAP ERP | Automated PO/invoice translation | Near real-time | Yes | Yes |
| Limit Type | Value | Platform | Notes |
|---|---|---|---|
| cXML document size | 5 MB | Ariba Network | Larger POs must split line items across multiple documents |
| Coupa REST API payload | 10 MB | Coupa | Applies to POST/PUT request body |
| Coupa paging limit | 50 records/page | Coupa | Use offset pagination; no cursor-based option |
| Jaggaer REST response | 1,000 records/page | Jaggaer ONE | Configurable per endpoint |
| OCI cart transfer | No formal limit | SAP OCI | Practical limit ~5,000 line items per cart session |
| Limit Type | Value | Window | Platform |
|---|---|---|---|
| Coupa API calls | 50 req/min (standard) | Per minute | Coupa — certified integrations get higher limits |
| Ariba REST API | Throttled / fair-use | Per org | SAP Ariba — no published hard limit; 429 returned on abuse |
| Jaggaer REST API | Customer-configurable | Per tenant | Jaggaer ONE — defaults vary by contract |
| CIG throughput | 1,000 IDocs/hour (default) | Per hour | CIG — tunable via BTP configuration |
| Ariba Network document processing | Near real-time | Continuous | Documents queue when supplier endpoints are down; retry 72h |
| Flow | Platform | Use When | Token Lifetime | Notes |
|---|---|---|---|---|
| cXML SharedSecret | Ariba, Coupa, Jaggaer | Document exchange (PO, invoice, punch-out) | Per-request (no session) | Credentials in cXML Header; HMAC validation |
| OAuth 2.0 Client Credentials | Coupa REST API | Server-to-server API calls | 24h access token | grant_type=client_credentials to /oauth2/token |
| OAuth 2.0 + API Key | SAP Ariba REST | Supplier mgmt, sourcing API | Session-based | Requires Ariba developer portal registration |
| OAuth 2.0 + SAML | Jaggaer REST | Enterprise SSO-enabled orgs | Configurable | SAML assertion exchanged for bearer token |
| OCI Session | SAP OCI | Punch-out from SAP ERP | Browser session | HTTP POST with HOOK_URL and OCI_VERSION params |
| CIG Certificate | SAP CIG | IDoc-to-cXML translation | Certificate-based | X.509 client cert on SAP BTP subaccount |
START — User needs to integrate ERP with SRM supplier portal
├── Which SRM platform?
│ ├── SAP Ariba
│ │ ├── ERP is SAP S/4HANA or ECC?
│ │ │ ├── YES → Use CIG (SAP Integration Suite, managed gateway)
│ │ │ └── NO → Use cXML direct or iPaaS middleware
│ │ └── Punch-out only?
│ │ ├── SAP ERP → OCI 5.0 (simpler, SAP-native)
│ │ └── Non-SAP ERP → cXML PunchOutSetupRequest
│ ├── Coupa
│ │ ├── Need real-time API access?
│ │ │ ├── YES → Coupa REST API (OAuth 2.0)
│ │ │ └── NO → cXML for documents, CSV/SFTP for bulk
│ │ └── Punch-out? → cXML PunchOutSetupRequest (Coupa-standard)
│ └── Jaggaer ONE
│ ├── cXML for transactional documents
│ ├── REST API for supplier management
│ └── OCI for SAP punch-out (if SAP ERP)
├── What scope?
│ ├── Punch-out catalog only → cXML PunchOutSetupRequest/Response + OrderMessage
│ ├── PO transmission → cXML OrderRequest
│ ├── PO + confirmation → cXML OrderRequest + ConfirmationRequest
│ ├── Full P2P → cXML: PunchOut + Order + ShipNotice + InvoiceDetail
│ └── Supplier onboarding / vendor master → REST API + MDM middleware
├── Integration method?
│ ├── cXML direct (point-to-point) → simplest, no middleware cost
│ ├── CIG (SAP-to-Ariba) → pre-built mappings, managed service
│ ├── iPaaS (MuleSoft, Boomi, Workato) → multi-platform, custom logic
│ └── EDI/VAN → legacy suppliers without cXML capability
└── Error tolerance?
├── Zero-loss → idempotency keys + dead letter queue + reconciliation
└── Best-effort → cXML with retry + email alerts on failure
| Step | Source | cXML Document | Target | ERP Object | Failure Handling |
|---|---|---|---|---|---|
| 1. Punch-out browse | ERP user | PunchOutSetupRequest | SRM portal | — | Timeout → retry; auth failure → re-register SharedSecret |
| 2. Cart return | SRM portal | PunchOutOrderMessage | ERP | Purchase Requisition | Parse error → reject cart; log cXML payload |
| 3. Requisition approval | ERP | — (internal) | ERP | Approved Requisition | — |
| 4. PO transmission | ERP | cXML OrderRequest | SRM portal | Purchase Order | 4xx → fix payload; 5xx → retry 3x with backoff |
| 5. Order confirmation | SRM portal | cXML ConfirmationRequest | ERP | PO Confirmation | Partial confirm → update PO lines; reject → alert buyer |
| 6. Advance Ship Notice | SRM portal | cXML ShipNoticeRequest | ERP | Goods Receipt (partial) | Missing ASN → manual receipt; duplicate → idempotency check |
| 7. Goods receipt | ERP | — (internal or ASN-triggered) | ERP | Goods Receipt | Receipt without PO → block; receipt > PO qty → tolerance check |
| 8. Invoice submission | Supplier | cXML InvoiceDetailRequest | SRM portal | Supplier Invoice | Validation fail → reject back to supplier with error codes |
| 9. Invoice flip to ERP | SRM portal | REST API / file / IDoc | ERP | AP Invoice | 3-way match fail → exception queue; tolerance → auto-approve |
| 10. Payment | ERP | — (payment run) | Bank / SRM | Payment Advice | Remittance advice via cXML PaymentRemittanceRequest (optional) |
Register your organization on the SRM platform's network (Ariba Network, Coupa Supplier Portal, or Jaggaer). Exchange cXML SharedSecrets with trading partners. For SAP-to-Ariba, deploy the CIG add-on on SAP BTP. [src1, src5]
# Test cXML connectivity to Ariba Network (ProfileRequest)
curl -X POST https://service.ariba.com/service/transaction/cxml.asp \
-H "Content-Type: text/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE cXML SYSTEM "http://xml.cxml.org/schemas/cXML/1.2.050/cXML.dtd">
<cXML payloadID="[email protected]" timestamp="2026-03-03T10:00:00+00:00">
<Header>
<From><Credential domain="NetworkId"><Identity>AN01234567890</Identity></Credential></From>
<To><Credential domain="NetworkId"><Identity>AN09876543210</Identity></Credential></To>
<Sender>
<Credential domain="NetworkId">
<Identity>AN01234567890</Identity>
<SharedSecret>your-shared-secret</SharedSecret>
</Credential>
<UserAgent>ERP-Integration/1.0</UserAgent>
</Sender>
</Header>
<Request><ProfileRequest/></Request>
</cXML>'
# Expected: HTTP 200 with ProfileResponse listing supported document types
Verify: HTTP 200 response with <ProfileResponse> containing supported transaction types.
When an ERP user clicks a supplier catalog link, the ERP sends a PunchOutSetupRequest. The supplier authenticates and returns a URL for browsing products. [src2, src6]
<!-- cXML PunchOutSetupRequest -- sent from ERP to SRM platform -->
<cXML payloadID="[email protected]"
timestamp="2026-03-03T10:05:00+00:00">
<Header>...credentials...</Header>
<Request deploymentMode="production">
<PunchOutSetupRequest operation="create">
<BuyerCookie>session-token-abc-123</BuyerCookie>
<BrowserFormPost>
<URL>https://erp.buyer.com/punchout/return</URL>
</BrowserFormPost>
<SupplierSetup>
<URL>https://supplier.example.com/cxml/punchout</URL>
</SupplierSetup>
</PunchOutSetupRequest>
</Request>
</cXML>
Verify: Response contains <PunchOutSetupResponse> with status 200 and <StartPage><URL> pointing to the supplier catalog.
When the user checks out on the supplier site, a PunchOutOrderMessage is POSTed back to the ERP. The ERP parses this to create a purchase requisition. [src2, src3]
Verify: ERP creates a purchase requisition with correct line items, quantities, and UNSPSC classifications.
After requisition approval, the ERP generates a PO and sends it as a cXML OrderRequest. [src1, src2]
Verify: SRM platform returns cXML Response with <Status code="200" text="OK"/>.
The supplier confirms the order via cXML ConfirmationRequest and later sends a ShipNoticeRequest (ASN). Both flow through the SRM network back to the ERP. [src1, src4]
Verify: PO status in ERP updates to "Confirmed" or "Exception".
The supplier submits an invoice via the SRM portal. The SRM platform validates it against the PO, then flips it to the ERP for 3-way matching. [src2, src7]
Verify: Invoice appears in ERP AP module. 3-way match passes within configured tolerance.
# Input: Raw cXML ConfirmationRequest from SRM platform
# Output: Updated PO status in ERP
from lxml import etree
import requests
def process_order_confirmation(cxml_payload: str, erp_api_url: str, erp_token: str):
root = etree.fromstring(cxml_payload.encode('utf-8'))
confirm_header = root.find('.//ConfirmationRequest/ConfirmationHeader')
po_number = confirm_header.get('invoiceID')
confirm_type = confirm_header.get('type') # accept, reject, backordered
line_confirmations = []
for item in root.findall('.//ConfirmationItem'):
line = {
'line_number': item.get('lineNumber'),
'quantity': item.get('quantity'),
'status': confirm_type
}
line_confirmations.append(line)
erp_response = requests.patch(
f"{erp_api_url}/purchase-orders/{po_number}/confirmations",
headers={"Authorization": f"Bearer {erp_token}",
"Content-Type": "application/json"},
json={"confirmations": line_confirmations}
)
erp_response.raise_for_status()
return erp_response.json()
# Input: Coupa API credentials, ERP API endpoint
# Output: Invoices created in ERP AP module
import requests, time
def flip_coupa_invoices_to_erp(coupa_url, coupa_token, erp_url, erp_token):
headers = {"Authorization": f"Bearer {coupa_token}", "Accept": "application/json"}
offset = 0
while True:
resp = requests.get(f"{coupa_url}/api/invoices", headers=headers,
params={"status": "approved", "exported": "false", "limit": 50, "offset": offset})
if resp.status_code == 429:
time.sleep(int(resp.headers.get('Retry-After', 60)))
continue
resp.raise_for_status()
invoices = resp.json()
if not invoices:
break
for inv in invoices:
erp_invoice = {
"vendor_id": inv["supplier"]["number"],
"invoice_number": inv["invoice-number"],
"po_number": inv["order-header-num"],
"total_amount": float(inv["total"]),
"lines": [{"po_line": l["order-line-num"], "quantity": float(l["quantity"]),
"unit_price": float(l["price"])} for l in inv["invoice-lines"]]
}
requests.post(f"{erp_url}/api/ap-invoices",
headers={"Authorization": f"Bearer {erp_token}"},
json=erp_invoice).raise_for_status()
requests.put(f"{coupa_url}/api/invoices/{inv['id']}",
headers={**headers, "Content-Type": "application/json"},
json={"exported": True})
offset += 50
# Input: Coupa instance URL, client credentials
# Output: OAuth 2.0 access token
curl -X POST "https://${COUPA_INSTANCE}.coupahost.com/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=${COUPA_CLIENT_ID}&client_secret=${COUPA_CLIENT_SECRET}&scope=core.read"
# Expected: {"access_token":"...","token_type":"bearer","expires_in":86400}
| SRM Field | ERP Field (SAP) | ERP Field (Oracle) | Type | Transform | Gotcha |
|---|---|---|---|---|---|
| Supplier Name | LFA1-NAME1 | HZ_PARTIES.PARTY_NAME | String | Direct | SAP max 35 chars; Oracle max 360 chars |
| Tax ID | LFA1-STCEG | ZX_REGISTRATIONS.REGISTRATION_NUMBER | String | Strip formatting | Validate format per country (VAT, EIN, etc.) |
| Bank Account | LFBK-BANKN | IBY_EXT_BANK_ACCOUNTS.BANK_ACCOUNT_NUM | String | Encrypted at rest | Never transmit in cXML — use separate secure channel |
| Payment Terms | LFB1-ZTERM | AP_TERMS_TL.NAME | Code/String | Map code tables | SRM and ERP payment term codes rarely match |
| Address | LFA1-STRAS/ORT01/PSTLZ | HZ_LOCATIONS.ADDRESS1/CITY/POSTAL_CODE | String | Split/merge fields | SAP has one street field; Oracle has 4 address lines |
| Currency | LFB1-WAERS | FND_CURRENCIES.CURRENCY_CODE | ISO 4217 | Direct | Validate against ERP's enabled currency list |
| Supplier Category | LFA1-KTOKK | POS_SUPPLIER_CLASSIFICATIONS | Code | Map to ERP category set | SRM categories are hierarchical; ERP may be flat |
| DUNS Number | LFA1-KRAUS | HZ_PARTIES.DUNS_NUMBER_C | String | Direct | Required for Ariba Network registration |
| cXML Element | SAP IDoc Segment | Oracle REST Field | Notes |
|---|---|---|---|
| OrderRequest.orderID | E1BPMEPOHEADER-PO_NUMBER | PoHeaderId (auto-generated) | SAP requires 10-char PO number; pad or truncate |
| ItemOut.quantity | E1BPMEPOITEM-QUANTITY | PoLineId.Quantity | Decimal precision varies — cXML allows 4, SAP allows 3 |
| UnitPrice.Money | E1BPMEPOITEM-NET_PRICE | PoLineId.UnitPrice | Currency must match PO header currency |
| Classification@UNSPSC | E1BPMEPOITEM-MATL_GROUP | PoLineId.CategoryId | Map UNSPSC to ERP material group / category |
| ShipTo.PostalAddress | E1BPMEPOHEADER-SUPPL_PLNT | ShipToLocationId | SRM sends full address; ERP expects location ID — requires lookup table |
| InvoiceDetailRequest.invoiceID | RBKP-BELNR | ApInvoiceId | ERP generates its own invoice number — store cross-reference |
| Code | Context | Meaning | Cause | Resolution |
|---|---|---|---|---|
| 401 | cXML Response | Unauthorized | SharedSecret mismatch or expired credentials | Re-exchange credentials with trading partner; verify ANID |
| 403 | cXML Response | Forbidden | Buyer-supplier relationship not enabled | Supplier must accept trading relationship invitation on SRM portal |
| 406 | cXML Response | Not Acceptable | Malformed cXML document | Validate against cXML DTD before sending; check UTF-8 encoding |
| 409 | Coupa REST | Conflict | Duplicate invoice number for same supplier | Implement idempotency — check for existing invoice before creating |
| 429 | Coupa REST | Rate limit exceeded | Too many API calls in window | Exponential backoff: wait Retry-After header seconds |
| 450 | Ariba Network | Document validation failed | Line items don't match contract/catalog pricing | Compare PO line prices against active contract |
| 500 | SRM Platform | Internal server error | Platform-side issue | Retry 3x with exponential backoff; escalate after 3 failures |
Extend ERP session timeout for punch-out sessions; implement client-side keepalive pings every 5 minutes. [src3]Build supplier enablement check into PO creation workflow — query API for supplier status before transmitting PO. [src8]Implement configurable match-hold window (48-72h) that retries matching after receipt posting; or use 2-way match for service POs. [src7]Enforce UTF-8 encoding in all cXML documents; strip or entity-encode non-UTF-8 characters at middleware layer. [src5]Use cXML payloadID as idempotency key; SRM platforms de-duplicate on payloadID. [src1]Always flip invoices in PO currency; handle FX conversion in SRM platform or middleware. [src2]# BAD -- polling every 5 minutes wastes API quota and misses real-time documents
while True:
invoices = coupa_api.get("/invoices", params={"status": "new"})
for inv in invoices:
process(inv)
time.sleep(300)
# GOOD -- Coupa/Ariba push documents to your endpoint in real-time
@app.route("/webhooks/coupa/invoice", methods=["POST"])
def handle_invoice_webhook(request):
payload = request.json
invoice = coupa_api.get(f"/invoices/{payload['id']}")
process_invoice(invoice)
return {"status": "received"}, 200
# BAD -- full extract of 50K suppliers every night
def nightly_sync():
all_suppliers = srm_api.get("/suppliers", params={"limit": 50000})
for s in all_suppliers:
erp_api.upsert_vendor(s)
# GOOD -- only sync suppliers modified since last sync
def delta_sync(last_sync_ts):
offset = 0
while True:
suppliers = srm_api.get("/suppliers",
params={"updated-at[gt]": last_sync_ts, "limit": 50, "offset": offset})
if not suppliers:
break
for s in suppliers:
erp_api.upsert_vendor(map_supplier_to_vendor(s))
offset += 50
<!-- BAD -- credentials in version control -->
<SharedSecret>MySecretPassword123</SharedSecret>
# GOOD -- credentials from secrets manager
import os
shared_secret = os.environ.get("ARIBA_SHARED_SECRET")
Maintain separate config profiles per environment; re-test supplier enablement in production. [src8]Build and maintain a UOM mapping table; validate UOM before sending any cXML document. [src3]Store integration capability per supplier in vendor master; route POs through appropriate channel. [src7]Validate every outbound cXML document against the official DTD before transmission. [src6]Parse ConfirmationItem-level quantities; update ERP PO line-by-line. [src1]Implement real-time price validation at PO creation; or set punch-out cart prices as binding. [src2]# Test cXML connectivity to Ariba Network (ProfileRequest health check)
curl -X POST https://service.ariba.com/service/transaction/cxml.asp \
-H "Content-Type: text/xml" \
-d '<?xml version="1.0"?><cXML><Header>...</Header><Request><ProfileRequest/></Request></cXML>'
# Test Coupa API authentication (OAuth 2.0)
curl -X POST "https://${COUPA_INSTANCE}.coupahost.com/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=${COUPA_CLIENT_ID}&client_secret=${COUPA_CLIENT_SECRET}"
# Check Coupa supplier integration status
curl -X GET "https://${COUPA_INSTANCE}.coupahost.com/api/suppliers?number=${SUPPLIER_NUMBER}" \
-H "Authorization: Bearer ${COUPA_TOKEN}" -H "Accept: application/json"
# Validate cXML document against DTD locally
xmllint --valid --dtdvalid cXML.dtd outbound_order.xml
# Test Jaggaer API connectivity
curl -X GET "https://${JAGGAER_INSTANCE}.jaggaer.com/api/v1/suppliers?limit=1" \
-H "Authorization: Bearer ${JAGGAER_TOKEN}" -H "Accept: application/json"
| Protocol/Platform | Version | Release Date | Status | Breaking Changes | Notes |
|---|---|---|---|---|---|
| cXML | 1.2.056 | 2025-06 | Current | None | Added PaymentRemittance enhancements |
| cXML | 1.2.050 | 2023-01 | Supported | None | Most widely deployed version |
| OCI | 5.0 | 2022-01 | Current | Added JSON support | SAP-only; backward compatible with OCI 4.0 |
| SAP CIG | Renamed to Integration Suite | 2025-H1 | Current | New BTP deployment model | Existing CIG deployments auto-migrated |
| Coupa REST API | R35 | 2025-12 | Current | Token-based supplier portal access | New OAuth scope requirements |
| Jaggaer REST API | 2024.2 | 2024-11 | Current | New supplier management endpoints | REST API still maturing |
| Use When | Don't Use When | Use Instead |
|---|---|---|
| 50+ suppliers on Ariba, Coupa, or Jaggaer needing automated PO/invoice flow | Fewer than 10 suppliers — manual portal entry is simpler | Direct email/PDF PO workflow |
| Real-time catalog pricing via punch-out | Static catalogs that change quarterly | Hosted catalog upload (CSV/BMEcat) |
| Automated 3-way match across procurement + ERP | Simple 2-way match (PO vs invoice) | Invoice-to-Pay AP Automation |
| Suppliers already registered on an SRM network | Need to build a custom supplier portal | Custom development with ERP APIs |
| Supplier onboarding, qualification, and risk scoring | Only vendor master data sync | Master Data Management for ERP |
| SAP ERP with SAP Ariba (CIG available) | Non-SAP ERP with Ariba (no CIG) | cXML direct or iPaaS middleware |
| Capability | SAP Ariba | Coupa | Jaggaer ONE | Notes |
|---|---|---|---|---|
| Punch-out protocol | cXML + OCI | cXML only | cXML + OCI | OCI only relevant for SAP ERPs |
| PO transmission | cXML, EDI, email | cXML, email, portal | cXML, EDI, email, portal | Ariba Network has largest supplier base |
| Invoice methods | cXML, portal, EDI | cXML, CSV, email/PDF (InvoiceSmash AI), portal | cXML, portal, email | Coupa InvoiceSmash AI auto-OCRs PDF invoices |
| Supplier network size | 5.3M+ companies | 10M+ companies | 4M+ companies | Network size = supplier adoption ease |
| REST API maturity | Moderate (newer) | High (comprehensive) | Growing (newer REST surface) | Coupa REST API is most complete |
| ERP pre-built connectors | CIG for SAP (native) | iPaaS connectors (Boomi, MuleSoft) | iPaaS connectors | Ariba-SAP has deepest native integration |
| Free supplier portal | Yes (Ariba Network) | Yes (Coupa Supplier Portal) | Yes (basic) | Supplier cost varies by transaction volume |
| Contract management | Yes (Ariba Contracts) | Yes (CLM module) | Yes (Contracts+) | All three support contract-backed catalogs |
| Supplier risk scoring | Yes (Ariba Risk) | Yes (Coupa Risk Assess) | Yes (Risk Management) | Third-party data integrations (D&B, Ecovadis) |
| AI/ML features | Ariba Spot Buy, guided buying | Coupa Pay, InvoiceSmash AI | Jaggaer AI (agentic sourcing) | Rapidly evolving; verify current capabilities |