<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Come</title>
    <description>The latest articles on DEV Community by Come (@sirenic).</description>
    <link>https://dev.to/sirenic</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4033871%2Fa4aad073-b3ff-45c3-aa60-288067456d06.png</url>
      <title>DEV Community: Come</title>
      <link>https://dev.to/sirenic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sirenic"/>
    <language>en</language>
    <item>
      <title>The e-invoicing mandate hits France on Sept 1. Here's the one call your agent needs before paying any French supplier</title>
      <dc:creator>Come</dc:creator>
      <pubDate>Mon, 17 Aug 2026 08:07:04 +0000</pubDate>
      <link>https://dev.to/sirenic/the-e-invoicing-mandate-hits-france-on-sept-1-heres-the-one-call-your-agent-needs-before-paying-5bdi</link>
      <guid>https://dev.to/sirenic/the-e-invoicing-mandate-hits-france-on-sept-1-heres-the-one-call-your-agent-needs-before-paying-5bdi</guid>
      <description>&lt;p&gt;On &lt;strong&gt;1 September 2026&lt;/strong&gt;, receiving an electronic invoice becomes mandatory for every&lt;br&gt;
VAT-liable company in France. Issuance is phased in behind it: large and mid-size&lt;br&gt;
companies from that same date, SMEs and micro-enterprises from &lt;strong&gt;1 September 2027&lt;/strong&gt;&lt;br&gt;
(art. 91, &lt;em&gt;Loi de finances pour 2024&lt;/em&gt;). Belgium's B2B mandate has been live since&lt;br&gt;
&lt;strong&gt;1 January 2026&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you are building an agent that pays suppliers, the mandate is not really your problem.&lt;br&gt;
What the mandate does is make a pre-existing gap impossible to ignore: &lt;strong&gt;your agent is&lt;br&gt;
about to send a structured invoice to, or move money towards, a legal entity it has never&lt;br&gt;
checked.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Three things your agent does not know
&lt;/h2&gt;

&lt;p&gt;Take a French supplier record coming out of your ERP, your CRM, or a PDF an LLM just&lt;br&gt;
parsed. It has a SIREN, a VAT number and an IBAN. Three questions are unanswered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Is the company still active?&lt;/strong&gt; A ceased company keeps a perfectly well-formed,
checksum-valid SIREN. Nothing in the string tells you it stopped trading in March.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is the intra-EU VAT number valid today?&lt;/strong&gt; Not last quarter, when someone exported the
supplier master file. Today. VIES is the only authority on that, and its answer moves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does that IBAN belong to a real, identified bank?&lt;/strong&gt; mod-97 tells you the string is
well-formed. It does not tell you the bank code maps to an institution that exists.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each question has a public answer. Getting all three usually means three integrations,&lt;br&gt;
two accounts and a SOAP client. That is the actual friction.&lt;/p&gt;
&lt;h2&gt;
  
  
  One call
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /v1/facturation/dossier?siren=552032534&amp;amp;iban=FR1420041010050500013M02606
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;$0.03. It returns the recipient's legal identity and obligation dates, the computed&lt;br&gt;
intra-EU VAT number &lt;strong&gt;checked live against VIES&lt;/strong&gt;, an IBAN check against official bank&lt;br&gt;
registries, and a deterministic verdict:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"verdict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pret_a_facturer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"raisons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pret_a_facturer&lt;/code&gt; is a boolean, and &lt;code&gt;raisons&lt;/code&gt; is a &lt;strong&gt;closed list&lt;/strong&gt;. Blocking reasons:&lt;br&gt;
&lt;code&gt;entreprise_cessee&lt;/code&gt;, &lt;code&gt;tva_invalide_vies&lt;/code&gt;, &lt;code&gt;tva_non_calculable&lt;/code&gt;, &lt;code&gt;iban_invalide&lt;/code&gt;.&lt;br&gt;
Informational ones: &lt;code&gt;diffusion_partielle&lt;/code&gt;, &lt;code&gt;tva_non_verifiable&lt;/code&gt;, &lt;code&gt;iban_non_fourni&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;banque_non_identifiee&lt;/code&gt;, &lt;code&gt;preparation_degradee&lt;/code&gt;. An agent branches on &lt;code&gt;code&lt;/code&gt;. It never&lt;br&gt;
parses prose, and it never has to ask a model what a sentence meant.&lt;/p&gt;

&lt;p&gt;One design decision worth stating because it cuts the other way: &lt;strong&gt;a VIES outage produces&lt;br&gt;
&lt;code&gt;tva_non_verifiable&lt;/code&gt; (informational), never a false &lt;code&gt;tva_invalide_vies&lt;/code&gt;.&lt;/strong&gt; An API that&lt;br&gt;
turns "the tax authority did not answer" into "this VAT number is invalid" will eventually&lt;br&gt;
make you refuse to pay a supplier who did nothing wrong.&lt;/p&gt;

&lt;p&gt;And since 16 August there is a second angle, for the invoice you &lt;em&gt;received&lt;/em&gt;:&lt;br&gt;
&lt;code&gt;GET /v1/facture/verifier?siren=&amp;amp;tva=&amp;amp;iban=&lt;/code&gt; ($0.02) cross-checks the identifiers printed&lt;br&gt;
on an invoice against each other — the French VAT key is deterministic, so a VAT number&lt;br&gt;
that is perfectly VIES-valid but belongs to &lt;strong&gt;another company&lt;/strong&gt; than the SIREN on the&lt;br&gt;
invoice is caught. A plain VIES check never sees that case.&lt;/p&gt;
&lt;h2&gt;
  
  
  The payment flow, in full
&lt;/h2&gt;

&lt;p&gt;There is no account and no API key. The first call returns 402 with a signable quote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"https://api.sirenic.eu/v1/facturation/dossier?siren=552032534"&lt;/span&gt;
&lt;span class="c"&gt;# HTTP/1.1 402 Payment Required&lt;/span&gt;
&lt;span class="c"&gt;# PAYMENT-REQUIRED: &amp;lt;base64 quote — amount, asset, network, expiry&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your agent signs that quote (USDC or EURC on Base, same numeric amount) and replays the&lt;br&gt;
request with a &lt;code&gt;PAYMENT-SIGNATURE&lt;/code&gt; header. Any x402 client does both steps for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;privateKeyToAccount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;viem/accounts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;wrapFetchWithPayment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@x402/fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;x402Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@x402/core/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;registerExactEvmScheme&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@x402/evm/exact/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;privateKeyToAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WALLET_KEY&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="s2"&gt;`0x&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;x402Client&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;registerExactEvmScheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payingFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wrapFetchWithPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.sirenic.eu/v1/facturation/dossier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;?siren=552032534&amp;amp;iban=FR1420041010050500013M02606&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;payingFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AbortSignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// exact bytes — see below&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Errors are never charged: the paywall quotes &lt;strong&gt;before&lt;/strong&gt; input validation, so a 400 or a&lt;br&gt;
404 costs nothing. With on-chain micro-payments you cannot refund, so "we don't charge for&lt;br&gt;
errors" has to be structural rather than a promise.&lt;/p&gt;
&lt;h2&gt;
  
  
  Verify the signature before you read the body
&lt;/h2&gt;

&lt;p&gt;Every paid 2xx response carries a detached Ed25519 signature in its headers. The signed&lt;br&gt;
message is rebuilt from the digest of the &lt;strong&gt;exact bytes you received&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sirenic-v1:{kid}:{timestamp}:{base64(sha256(body))}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createPublicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;verify&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;verifySignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;publicKeyBase64&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`sirenic-v1:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createPublicKey&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicKeyBase64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;der&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;spki&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;kid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-sirenic-key-id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-sirenic-timestamp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-sirenic-signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.sirenic.eu/.well-known/sirenic-signing-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;verifySignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invalid signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// only now&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The order matters, and it is the one mistake I see most often: &lt;code&gt;JSON.parse&lt;/code&gt; then&lt;br&gt;
&lt;code&gt;JSON.stringify&lt;/code&gt; reorders or reformats something, the digest changes, and the signature&lt;br&gt;
fails for a payload that was never tampered with. Keep the bytes. Verify. &lt;strong&gt;Then&lt;/strong&gt; parse.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why the signed response is an audit exhibit
&lt;/h2&gt;

&lt;p&gt;The interesting part is not the signature on its own — it is what sits &lt;strong&gt;inside&lt;/strong&gt; the&lt;br&gt;
signed bytes. Each response carries a &lt;code&gt;provenance&lt;/code&gt; array: for every block of data, the&lt;br&gt;
official register it came from, its licence, and its &lt;code&gt;as_of&lt;/code&gt; date with the exact meaning&lt;br&gt;
of that date.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;destinataire.destinataire   INSEE Sirene                    stock      2026-07-01 (publication_officielle)
destinataire.tva            Calcul Sirenic                  calcul    no date (computed block)
tva_vies                    VIES (European Commission)      temps_reel 2026-08-11T09:14:22Z (consultation)
banque.banque               REGAFI (ACPR / Banque de France) temps_reel 2026-08-11T09:14:23Z (consultation)
banque.banque.bic           GLEIF BIC-to-LEI mapping        temps_reel 2026-08-11T09:14:23Z (consultation)
verdict                     Sirenic rules                   calcul    no date (computed block)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(abridged — the exact blocks depend on what was actually consulted; no IBAN means no bank&lt;br&gt;
register is declared, because we do not claim to have read a register we never opened.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Three properties fall out of this, and they are the reason the whole thing exists:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A computed block never carries a date.&lt;/strong&gt; &lt;code&gt;mode: "calcul"&lt;/code&gt; means Sirenic derived it from
the other blocks. Stamping "now" on a verdict would suggest a freshness that belongs to
its inputs, not to it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A real-time block carries the date of the consultation that actually happened&lt;/strong&gt; — the
timestamp of the call we actually made to VIES, stored alongside the cached answer, not
the moment you opened the JSON. A cached answer from an hour ago was not consulted "now".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An unavailable source says so.&lt;/strong&gt; When VIES is down, the &lt;code&gt;tva_vies&lt;/code&gt; entry exists with
&lt;code&gt;precision_as_of: "indisponible"&lt;/code&gt; and no date. Absence of an answer is recorded as
absence of an answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So: the payload is authenticated, and it states where every field came from and when. That&lt;br&gt;
is what an auditor asks for, months later, about a payment you made in August.&lt;/p&gt;
&lt;h2&gt;
  
  
  The archive that re-verifies without us
&lt;/h2&gt;

&lt;p&gt;If provenance lives inside the signed body, you can store four files and throw the API&lt;br&gt;
away:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;dossier-facturation-552032534-2026-08-11T09-14-22Z/
├── reponse.json        the exact bytes — never reformat them
├── signature.txt       kid, timestamp, Ed25519 signature (base64)
├── cle-publique.json   the public key as published, at the time
└── LISEZ-MOI.md        the commands to re-check, offline
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Re-checking, months later, with Node and nothing else — no network, no dependency, no&lt;br&gt;
Sirenic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'
const fs=require("fs"), c=require("crypto");
const corps=fs.readFileSync("reponse.json");
const s={}; for (const l of fs.readFileSync("signature.txt","utf8").trim().split("\n")) s[l.slice(0,l.indexOf("="))]=l.slice(l.indexOf("=")+1);
const cle=JSON.parse(fs.readFileSync("cle-publique.json","utf8")).public_key;
const message="sirenic-v1:"+s.kid+":"+s.timestamp+":"+c.createHash("sha256").update(corps).digest("base64");
const pub=c.createPublicKey({key:Buffer.from(cle,"base64"),format:"der",type:"spki"});
console.log(c.verify(null,Buffer.from(message),pub,Buffer.from(s.signature,"base64")) ? "VALID" : "INVALID");
'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An &lt;code&gt;openssl pkeyutl -verify&lt;/code&gt; variant is in the archive's README for machines without Node.&lt;br&gt;
Change one byte of &lt;code&gt;reponse.json&lt;/code&gt; and both fail.&lt;/p&gt;

&lt;p&gt;The runnable version of everything above — pay, verify, read, write the archive, then&lt;br&gt;
re-verify &lt;strong&gt;from the written files only&lt;/strong&gt; — is one file:&lt;br&gt;
&lt;a href="https://github.com/sirenic-eu/sirenic-examples/blob/main/examples/verify-invoice-file.ts" rel="noopener noreferrer"&gt;&lt;code&gt;examples/verify-invoice-file.ts&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
An archive that needs the API to be re-checked is not an archive, so the script fails hard&lt;br&gt;
if the round trip through disk does not verify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Belgium and Poland, because your suppliers are not all French
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;GET /v1/eu/facturation/dossier?pays=&amp;amp;id=&amp;amp;iban=&lt;/code&gt; — same $0.03, same verdict shape.&lt;/p&gt;

&lt;p&gt;Belgium: registry identity, VAT against VIES, and Peppol reachability. The mandate has&lt;br&gt;
been live since 1 January 2026.&lt;/p&gt;

&lt;p&gt;Poland is the one worth knowing about even if you never invoice a Polish company, because&lt;br&gt;
it is the case where &lt;strong&gt;verifying a bank account has a direct tax consequence&lt;/strong&gt;. The&lt;br&gt;
&lt;em&gt;wykaz podatników VAT&lt;/em&gt; — the official White List — records which bank accounts a taxpayer&lt;br&gt;
has declared. Paying more than &lt;strong&gt;15,000 PLN&lt;/strong&gt; into an account that is not on that list&lt;br&gt;
costs the buyer the VAT deduction and creates joint liability for the supplier's unpaid&lt;br&gt;
VAT (art. 117ba, &lt;em&gt;Ordynacja podatkowa&lt;/em&gt;). So the response tells you whether the IBAN you are&lt;br&gt;
about to pay is DECLARED by that taxpayer, and the provenance entry carries the date the&lt;br&gt;
White List was actually queried — which is the date that makes the check provable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two limits, stated in every response
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This is not a payee verification.&lt;/strong&gt; The account holder's name is never checked. Every&lt;br&gt;
IBAN response says so in the payload itself (&lt;code&gt;verification_titulaire: non_disponible&lt;/code&gt;). If&lt;br&gt;
an invoice tells you to pay "ACME SARL" and the IBAN belongs to an identified French bank,&lt;br&gt;
we confirm the second half of that sentence and say nothing about the first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sirenic is not an accredited platform (PDP/PA).&lt;/strong&gt; It has no access to the restricted&lt;br&gt;
central directory, and it never issues, transmits, converts or routes invoices. It answers&lt;br&gt;
questions about a company before you invoice or pay it. Choosing your PDP is a separate&lt;br&gt;
decision, and this is not it.&lt;/p&gt;

&lt;p&gt;The verdict is deterministic decision support with closed-list reasons traced to their&lt;br&gt;
source. It is not tax-compliance advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free things to poke at first
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /v1/reperer?texte=…&lt;/code&gt; — detects SIREN / SIRET / VAT / LEI in raw text (checksum
validated) and returns the recommended call with its price. Free.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /v1/provenance/registres&lt;/code&gt; — the join table for every &lt;code&gt;source_code&lt;/code&gt; you will see in
a &lt;code&gt;provenance&lt;/code&gt; array: register, authority, country, mode, licence, URL. Free.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://api.sirenic.eu/llms.txt&lt;/code&gt; — the whole catalogue written for a model rather than
for a human.&lt;/li&gt;
&lt;li&gt;MCP, if your agent speaks it: &lt;code&gt;https://api.sirenic.eu/mcp&lt;/code&gt; (streamable HTTP). Every tool
takes an optional &lt;code&gt;x_payment&lt;/code&gt; argument; call without it and you get the signable quote
back, so the discovery flow costs nothing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The six routes in scope here: &lt;code&gt;/v1/facturation/dossier&lt;/code&gt; ($0.03),&lt;br&gt;
&lt;code&gt;/v1/eu/facturation/dossier&lt;/code&gt; ($0.03), &lt;code&gt;/v1/facture/verifier&lt;/code&gt; ($0.02),&lt;br&gt;
&lt;code&gt;/v1/entreprise/{siren}/facturation-prep&lt;/code&gt; ($0.02), &lt;code&gt;/v1/iban/verifier/{iban}&lt;/code&gt; ($0.005),&lt;br&gt;
&lt;code&gt;/v1/tva/verifier/{numero}&lt;/code&gt; ($0.003). All except &lt;code&gt;facture/verifier&lt;/code&gt; carry the&lt;br&gt;
&lt;code&gt;provenance&lt;/code&gt; array inside the signed body.&lt;/p&gt;

&lt;p&gt;Happy to answer anything about the mandate timeline, the closed-list verdict, or the x402&lt;br&gt;
flow. If you find a case where the verdict is wrong — a company we call active that isn't,&lt;br&gt;
an IBAN we resolve to the wrong bank — that is the feedback I want most.&lt;/p&gt;

</description>
      <category>api</category>
      <category>typescript</category>
      <category>ai</category>
      <category>payments</category>
    </item>
    <item>
      <title>Build a KYB agent in 20 lines no API key, the agent pays per call</title>
      <dc:creator>Come</dc:creator>
      <pubDate>Sat, 18 Jul 2026 17:06:30 +0000</pubDate>
      <link>https://dev.to/sirenic/build-a-kyb-agent-in-20-lines-no-api-key-the-agent-pays-per-call-334p</link>
      <guid>https://dev.to/sirenic/build-a-kyb-agent-in-20-lines-no-api-key-the-agent-pays-per-call-334p</guid>
      <description>&lt;p&gt;AI agents have a boring problem: they can't sign up for things. Every&lt;br&gt;
interesting API sits behind an email verification, a credit-card form and an&lt;br&gt;
API key — three things a headless agent can't do. I ran into this building my&lt;br&gt;
own agents, so I ended up building an API the other way round, and this is the&lt;br&gt;
20-line version of what it makes possible.&lt;/p&gt;

&lt;p&gt;We'll build a working KYB (Know Your Business) agent in ~20 lines of&lt;br&gt;
TypeScript. It verifies any French company — identity, officers, insolvency&lt;br&gt;
alerts, filed financials, sanctions screening — and it pays for what it uses,&lt;br&gt;
per call, in USDC. No account anywhere.&lt;/p&gt;
&lt;h2&gt;
  
  
  The building blocks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/x402-foundation/x402" rel="noopener noreferrer"&gt;x402&lt;/a&gt;&lt;/strong&gt; — an open protocol
that revives HTTP 402: the server answers a machine-readable payment
quote; the client signs a stablecoin authorization and retries. One
round-trip, no gas for the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://api.sirenic.eu" rel="noopener noreferrer"&gt;Sirenic&lt;/a&gt;&lt;/strong&gt; — a pay-per-call API over official
French/European company registers (INSEE/INPI open data, Etalab 2.0
license). Prices from $0.001. Disclosure: I built it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Step 1 — look at a quote (no wallet yet)
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"https://api.sirenic.eu/v1/kyb/552032534"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You get &lt;code&gt;HTTP 402 Payment Required&lt;/code&gt;. The &lt;code&gt;PAYMENT-REQUIRED&lt;/code&gt; header is&lt;br&gt;
base64 JSON: price (&lt;code&gt;$0.15&lt;/code&gt; → &lt;code&gt;150000&lt;/code&gt; atomic USDC units), the receiving&lt;br&gt;
address, the official USDC contract and the network (Base). Nothing was&lt;br&gt;
charged — quotes are free.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2 — a wallet for your agent
&lt;/h2&gt;

&lt;p&gt;Create a throwaway wallet (MetaMask works), export the private key, and&lt;br&gt;
fund it with a couple of dollars of USDC on &lt;strong&gt;Base&lt;/strong&gt; (any exchange can&lt;br&gt;
withdraw to Base). The &lt;code&gt;exact&lt;/code&gt; payment scheme uses signed authorizations,&lt;br&gt;
so the client never pays gas — USDC is all the wallet needs. The whole&lt;br&gt;
tutorial costs $0.15.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @x402/fetch @x402/core @x402/evm viem tsx
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TEST_WALLET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — the agent
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;privateKeyToAccount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;viem/accounts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;wrapFetchWithPayment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@x402/fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;x402Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@x402/core/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;registerExactEvmScheme&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@x402/evm/exact/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;siren&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;552032534&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;x402Client&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;registerExactEvmScheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;privateKeyToAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEST_WALLET_KEY&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="s2"&gt;`0x&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payingFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wrapFetchWithPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;kyb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;payingFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`https://api.sirenic.eu/v1/kyb/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;siren&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Company : &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;kyb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;denomination&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;kyb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;siren&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) — &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;kyb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;etat_administratif&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Officers: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;kyb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dirigeants&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; | VAT: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;kyb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tva_intracommunautaire&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Alerts  : &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;kyb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alertes_bodacc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;procedures_collectives&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;n/a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; insolvency proceedings`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Sanctions screening: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;kyb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;criblage_sanctions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statut&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Completeness: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;kyb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score_completude&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/100`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;npx tsx kyb-agent.ts 552032534
&lt;span class="go"&gt;Company : DANONE (552032534) — actif
Officers: 13 | VAT: FR27552032534
Alerts  : 0 insolvency proceedings
Sanctions screening: correspondances_a_verifier
Completeness: 100/100
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole agent. &lt;code&gt;wrapFetchWithPayment&lt;/code&gt; intercepted the 402, signed&lt;br&gt;
a $0.15 USDC authorization, retried, and the API released the response once&lt;br&gt;
the settlement was confirmed on-chain.&lt;/p&gt;
&lt;h2&gt;
  
  
  What actually happened in that one call
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;/v1/kyb/{siren}&lt;/code&gt; endpoint consolidates six sources server-side:&lt;br&gt;
registry identity and officers, BODACC legal announcements, filed accounts,&lt;br&gt;
and a fuzzy sanctions screening of the company name &lt;strong&gt;and every officer&lt;/strong&gt;&lt;br&gt;
against the  six official lists (UN, EU, OFAC, UK, French asset-freeze register, Swiss SECO). Matches come back with a 0-100 confidence score and the matched alias — never a bare yes/no, because "Emmanuel Macron vs. Emmanuel Sultani&lt;br&gt;
Makenga" is exactly the kind of thing you want a human to look at.&lt;/p&gt;

&lt;p&gt;Two production details worth stealing for your own paid APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Never charge for errors.&lt;/strong&gt; If the company doesn't exist (404) or a
source fails (503), the verified payment is cancelled — the agent pays
nothing. Agents can't file support tickets; your error semantics are
your support team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A completeness score beats silent gaps.&lt;/strong&gt; If one upstream source is
down, the response says which block is missing and why, and the score
drops. The agent can decide whether 80/100 is good enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  MCP, if you'd rather not write code
&lt;/h2&gt;

&lt;p&gt;The same API is an MCP server — Claude and Cursor can use it directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http sirenic https://api.sirenic.eu/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each tool takes an optional &lt;code&gt;x_payment&lt;/code&gt; parameter with the same quote/sign/&lt;br&gt;
retry loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Examples repo (this code + more): &lt;a href="https://github.com/sirenic-eu/sirenic-examples" rel="noopener noreferrer"&gt;https://github.com/sirenic-eu/sirenic-examples&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenAPI: &lt;a href="https://api.sirenic.eu/openapi.json" rel="noopener noreferrer"&gt;https://api.sirenic.eu/openapi.json&lt;/a&gt; · llms.txt: &lt;a href="https://api.sirenic.eu/llms.txt" rel="noopener noreferrer"&gt;https://api.sirenic.eu/llms.txt&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Data is official open data redistributed as published; responses carry&lt;br&gt;
source and disclaimer fields. Screening output is a decision aid, not a&lt;br&gt;
compliance opinion.&lt;/p&gt;

&lt;p&gt;Update, July 22: Sirenic is live on Product Hunt today. Since this article was written, paid responses are Ed25519 signed (verify any answer offline against /.well-known/sirenic-signing-key), you can pay in EURC as well as USDC, and search results now include a 0-1 confidence score. If you want to follow along, try it, or leave feedback on the pay-per-call model, here is the link: &lt;a href="https://www.producthunt.com/products/sirenic?launch=sirenic" rel="noopener noreferrer"&gt;https://www.producthunt.com/products/sirenic?launch=sirenic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Update, July 22: Sirenic is live on Product Hunt today. Since this article was written, paid responses are Ed25519 signed (verify any answer offline against /.well-known/sirenic-signing-key), you can pay in EURC as well as USDC, and search results now include a 0-1 confidence score. &lt;/p&gt;

</description>
      <category>ai</category>
      <category>typescript</category>
      <category>api</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
