Repository navigation
Releases
facta-dte/api v0.2.0-rc.3
Additive only: every existing call, type and default keeps working. Follows the TypeScript SDK (Facta-DTE/facta-api-sdk#73).
Added
- The temporary copy and the storage confirmation. Facta keeps an encrypted temporary copy of every sealed document and deletes it 2 days after the document is marked as stored, or 30 days after sealing if nobody marks it. New:
listUnconfirmedDocuments()(GET /v1/storage/unconfirmed),confirmDocumentStorage($code, ['where' => ..., 'sha256' => ...|'bytes' => ...])(POST /v1/dte/{code}/storage-confirmation; throwsstorage_unsupportedwithout posting on a server that does not advertisecapabilities.storageConfirmation) andstoreAndConfirmUnconfirmed(['store' => callable, ...]), which downloads each document, hands it to your callable and confirms with the SHA-256 of the stored bytes; it returns a report and never throws for one document. FactaError::TEMPORARY_COPY_EXPIRED(410),NOTHING_TO_REPAIR_FROM(409) andSTORAGE_HASH_MISMATCH(422). None is retried.getStorageStatus()passes throughcapabilities.storageConfirmationandsinConfirmar;getDocumentCopies()passes throughconfirmado.tests/fixtures/openapi.yamlrefreshed with the two routes and the three codes (ContractParityTest).
No /byos report gate exists in this SDK (BYOS destinations are not implemented yet), so the === 1 capability bug fixed in the TypeScript SDK has no counterpart here.
Facta::PRODUCTION_BASE_URLnames the existing production endpoint and aliasesFacta::DEFAULT_BASE_URL; the production default and opt-inFacta::STAGING_BASE_URLremain unchanged.
facta-dte/api v0.2.0-rc.2
Release candidate (pre-release tag; the stable release stays at 0.1.0). Additive only: every call, constant, option and return shape of 0.1.0 and 0.2.0-rc.1 keeps working (tests/BackwardCompatTest.php). Matches the staging API of 29-Oct-2026.
Added
items[].montoDescu(discount of a line in money),pagoswith the newPagovalue object (plain arrays still work),items[].tipoVentawith theSaleTypeconstants, andaplicarRetencionIva(01, 03) /aplicarPercepcionIva(03). The request is still a plain array that travels untouched: the server owns every rule.campo_desconocidowarnings:issue(),prepare(),sign()andissueDraft()results gainunknownFields(campo,mensaje,sugerencia) andfactaWarning(the parsedFacta-Warningheader) only when there is something to report.FactaError::getUnknownFields()readsdetails.advertenciasandgetFactaWarning()the header. Unknown fields stay warnings: they are never rejected.Facta::STAGING_BASE_URL. The default base URL is still production.FactaErrortakes an optional sixth constructor argument (the header); existing calls are unchanged.
facta-dte/api v0.2.0-rc.1
Release candidate. Tagged as a pre-release: Composer's default stable stability and constraints such as ^0.1 do not select it. Install it explicitly with composer require facta-dte/api:0.2.0-rc.1. The stable release stays at 0.1.0 until the shared data key is in production. The notes below are the planned 0.2.0 notes.
The shared data key: the server resolves catalogue ids and reads the catalogue in every mode, so the
SDK carries no copy of it. Follows the TypeScript SDK 0.6.0.
Changed
- Catalogue reads always call the API (
listCustomers()...searchProducts()). They no longer
read/v1/statusfirst, no longer needunlockKey, and no longer throwunauthorized
(local_snapshot_unsupported/unlock_key_missing) for a company whose catalogue is encrypted at rest. customerId/productIdinissue()andprepare()travel untouched. The same gate no
longer refuses them. An unknown id is the server'snot_found(details.reasoncatalog_unknown_id).- No
X-Facta-Request-Fingerprintheader onissue()/prepare(): the SDK does not rewrite the
body, so the server's idempotency claim hashes it.Json\Fingerprintstays for other uses. diagnose()no longer reports acatalog-synccheck. The guidance ofcatalog_encryptedno longer
says the catalogue can be read with the unlock key.
Added
- Error codes the server now returns:
catalog_revision_conflict,catalog_record_inactive,
catalog_search_too_broad,catalog_cursor_expired,data_grant_unavailable,data_grant_revoked,
data_epoch_stale,inbox_retired(constants onFactaError). - Drafts (scope
draft):createDraft(),listDrafts(),getDraft(),getDraftByKey(),updateDraft()(versionsent asIf-Match),cancelDraft()andissueDraft()(carries the sign key, answers likeissue()plusborradorId). Constants for the draft codes (DRAFTS_NOT_READY,VERSION_MISMATCH,DRAFT_CLAIMED,DRAFT_ISSUING,DRAFT_ISSUED,DRAFT_NOT_ISSUABLE,ITEM_FINAL,ID_CONFLICT,INBOX_CAP_REACHED,CONTENT_HASH_MISMATCH,CATALOG_WRITE_TEST_KEY). - Every code the server can answer is a
FactaErrorconstant, includingMH_AUTH_FAILED(424),PAID_PLAN_REQUIRED(402) and the Drive broker codes.CATALOG_SNAPSHOT_RETIREDis removed: the server never sent it. tests/ContractParityTest.phppins the SDK to a snapshot of the staging contract (tests/fixtures/openapi.yaml): error codes both ways, the retry set (the TypeScript one) and every route wrapped or deliberately not wrapped.ifMatchoption onupdateCustomer()/updateProduct(), andrevisionon single catalogue records
read or written on a company that shares its data key (fromFacta-Catalog-Revision/ETag).listCatalogChanges()forGET /v1/catalog/changes?since=.FactaError::WEBHOOK_LIMIT_REACHED,WEBHOOK_DUPLICATE;Webhook::EVENTS;WebhookVerificationError(withgetReason()) thrown byWebhook::constructEvent().data_grant_unavailableanddata_epoch_stale(503) are retried like the other transient codes;data_grant_revoked(403) is final.- Webhooks:
createWebhook(),listWebhooks(),getWebhook(),updateWebhook(),deleteWebhook(),
rotateWebhookSecret(),testWebhook(), and the receiver-sideWebhook::verify()/
Webhook::constructEvent()/Webhook::sign()withWebhookReplayStore/MemoryWebhookReplayStore.
facta-dte/api v0.1.0
Changed
status()returns an associative array, like every other method (it returned a\stdClass).
Not yet
- Local catalog snapshots, BYOS destinations and the restart-safe archive: they need the unlock
key. Until then, reading an encrypted catalog or issuing withcustomerId/productId
against one throwsunauthorized(details.reason).
Added
- Tier 1, everything that needs only HTTP:
region(),servedRegion(),diagnose(),
issue()(caller-ownedidempotencyKey,deliver),prepare()/sign(),
getDocumentStatus(),listDocuments(), byte-exactdownloadDocument()(json,pdf,
ticket),invalidate(),registerReturn(),deliverEmail(),deliverWhatsApp(),
getDelivery(),waitForDelivery(),getStorageStatus(),getDocumentCopies(),
retryDocumentStorage(),listHolding(),getContract()and the catalog reads and
writes (listCustomers()...deactivateProduct()). X-Facta-Request-Fingerprint(fingerprint-v1 of the unresolved request,entregaincluded) on
POST /v1/dteandPOST /v1/dte/prepareonly.- Storage receipts are checked against the contract and against the bytes in the same answer; a
receipt that does not match is replaced bystorageErrorCode. - Live integration against the staging API:
scripts/live-integration.php, its guards and report
(scripts/live/), and the workflowsdk-live-integration.ymlwith a sticky pull-request comment. - Package skeleton:
facta-dte/api, namespaceFacta\Api, PHP 7.4 and later,
no required Composer dependency. Factaclient withstatus()(GET /v1/status) and the internal request loop:
retries ofidempotency_in_flightandmh_unreachablehonouringRetry-After,
per-attempt timeouts,x-region,X-Facta-KeyandX-Facta-Sign-Key(only on
the routes that sign), oneIdempotency-Keyper call reused across retries.FactaErrorwith the error codes of the TypeScript SDK and secret redaction.- HTTP transports: cURL, PHP streams (fallback without
ext-curl) and PSR-18. Json\Jcs(RFC 8785) andJson\Fingerprint::v1(), validated against the shared
conformance vectors offacta-api-sdk.autoload.phpandscripts/build-zip.phpfor installs without Composer.