<?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: Kynth</title>
    <description>The latest articles on DEV Community by Kynth (@kynth).</description>
    <link>https://dev.to/kynth</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%2F4016803%2Ffe9c86b5-5239-46d6-a16b-6b8dd56b1e5c.png</url>
      <title>DEV Community: Kynth</title>
      <link>https://dev.to/kynth</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kynth"/>
    <language>en</language>
    <item>
      <title>Any document in. Clean JSON out. — how I turned my apps' AI layer into a 39-endpoint API</title>
      <dc:creator>Kynth</dc:creator>
      <pubDate>Wed, 08 Jul 2026 13:26:48 +0000</pubDate>
      <link>https://dev.to/kynth/any-document-in-clean-json-out-how-i-turned-my-apps-ai-layer-into-a-39-endpoint-api-40pc</link>
      <guid>https://dev.to/kynth/any-document-in-clean-json-out-how-i-turned-my-apps-ai-layer-into-a-39-endpoint-api-40pc</guid>
      <description>&lt;p&gt;I run a suite of AI products, and every one of them kept re-implementing the same jobs: parse an invoice, normalize a bank statement, redact PII before it hits a log, fight a chargeback. Prompt-level extraction fails silently — the JSON comes back malformed or subtly wrong just often enough that every app grew its own retry/validation/normalization layer.&lt;/p&gt;

&lt;p&gt;So I pulled that whole layer out into one API. It's called &lt;strong&gt;Kynth Core&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The spearhead: documents
&lt;/h2&gt;

&lt;p&gt;Nine document engines, each committed to &lt;strong&gt;one document kind&lt;/strong&gt; — that commitment is what makes the output schema-valid and field-accurate instead of "usually fine":&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Job&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;invoice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Invoice → vendor, dates, PO refs, tax, line items&lt;/td&gt;
&lt;td&gt;$0.08/invoice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;receipt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Receipt → merchant, items, totals, expense category&lt;/td&gt;
&lt;td&gt;$0.06/receipt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;statement&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bank/card statement → every transaction, normalized&lt;/td&gt;
&lt;td&gt;$0.12/statement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;resume&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resume → structured candidate profile&lt;/td&gt;
&lt;td&gt;$0.08/resume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tables&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any document → every table as clean headers + rows&lt;/td&gt;
&lt;td&gt;$0.08/document&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;split&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multi-doc scan bundle → classified, split documents&lt;/td&gt;
&lt;td&gt;$0.10/bundle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;compare&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Two contract versions → material changes + risk&lt;/td&gt;
&lt;td&gt;$0.15/comparison&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contract&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Contract → parties, term, renewal, obligations, risks&lt;/td&gt;
&lt;td&gt;$0.12/contract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;parse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anything else → structured, validated JSON&lt;/td&gt;
&lt;td&gt;$0.10/document&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Flat price per document — a 9-page statement costs the same as a 1-pager. And the accuracy isn't a vibe: &lt;strong&gt;every endpoint's field-level score is public at &lt;a href="https://api.kynth.studio/benchmarks" rel="noopener noreferrer"&gt;api.kynth.studio/benchmarks&lt;/a&gt;&lt;/strong&gt;, run against the live production API and re-run on every prompt or model-routing change.&lt;/p&gt;

&lt;h2&gt;
  
  
  …and 30 more jobs on the same wallet
&lt;/h2&gt;

&lt;p&gt;The catalog behind the spearhead, browsed at &lt;a href="https://api.kynth.studio/endpoints" rel="noopener noreferrer"&gt;/endpoints&lt;/a&gt;: guaranteed-schema extraction (&lt;code&gt;structure&lt;/code&gt; — your JSON Schema in, conforming output out, retries handled), entity matching, batch categorization, ticket triage, meeting minutes, reply drafting, collections sequences, 3-way PO matching, fraud triage, moderation &lt;strong&gt;against your own policy&lt;/strong&gt;, brand-voice rewriting, a cited web-research brief, lead screening, agent memory (semantic store/search, no vector DB to run), transcription, image generation, and TTS.&lt;/p&gt;

&lt;p&gt;Every endpoint is a finished job with its own page and its own per-task price — never a prompt you have to engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The design decisions I actually care about
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Priced per task, not per token.&lt;/strong&gt; You know what a document costs before you send it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You're only charged on success.&lt;/strong&gt; The charge happens &lt;em&gt;after&lt;/em&gt; a valid result, in the same row-locked transaction that writes the usage ledger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model routing is internal.&lt;/strong&gt; Each task runs on the cheapest model that's good enough (Gemini / Claude / GPT, chosen per job, re-tuned as models improve). You never configure it; you just get the price on the tin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;500 free credits every month, no card.&lt;/strong&gt; Enough to ship a real integration before you pay anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to call it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.kynth.studio/v1/invoice &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer ksk_live_…"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{ "fileUrl": "https://…/invoice.pdf" }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;TypeScript:&lt;/strong&gt;&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;KynthCore&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;@kynth/api&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;kynth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;KynthCore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&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;KYNTH_API_KEY&lt;/span&gt;&lt;span class="o"&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;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;kynth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;fileUrl&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="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balanceRemaining&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Python:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;kynth&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Kynth&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Kynth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ksk_live_...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://.../invoice.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;And an MCP server&lt;/strong&gt;, so Claude or Cursor call all 39 endpoints as native tools:&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 kynth-core &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KYNTH_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ksk_live_… &lt;span class="nt"&gt;--&lt;/span&gt; npx &lt;span class="nt"&gt;-y&lt;/span&gt; @kynth/api-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDKs and MCP server are generated from the same catalog the OpenAPI spec is built from, so they can't drift from the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Home:&lt;/strong&gt; &lt;a href="https://api.kynth.studio" rel="noopener noreferrer"&gt;https://api.kynth.studio&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All 39 endpoints:&lt;/strong&gt; &lt;a href="https://api.kynth.studio/endpoints" rel="noopener noreferrer"&gt;https://api.kynth.studio/endpoints&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmarks:&lt;/strong&gt; &lt;a href="https://api.kynth.studio/benchmarks" rel="noopener noreferrer"&gt;https://api.kynth.studio/benchmarks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive reference:&lt;/strong&gt; &lt;a href="https://api.kynth.studio/reference" rel="noopener noreferrer"&gt;https://api.kynth.studio/reference&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAPI spec:&lt;/strong&gt; &lt;a href="https://api.kynth.studio/openapi.json" rel="noopener noreferrer"&gt;https://api.kynth.studio/openapi.json&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd genuinely like feedback on the endpoint set — which of these you'd actually reach for over rolling your own against a raw model, and what's missing. Reply here or poke at the API.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>webdev</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Modeling Prior Authorization as a Payer-Policy Matching Problem</title>
      <dc:creator>Kynth</dc:creator>
      <pubDate>Mon, 06 Jul 2026 16:03:02 +0000</pubDate>
      <link>https://dev.to/kynth/modeling-prior-authorization-as-a-payer-policy-matching-problem-14o3</link>
      <guid>https://dev.to/kynth/modeling-prior-authorization-as-a-payer-policy-matching-problem-14o3</guid>
      <description>&lt;p&gt;It's 4:47 PM. There are nine prior authorizations in the queue and the person working them has been at it since lunch. Each one is the same ritual: open the chart, hunt for the diagnosis codes, cross-reference the payer's coverage policy PDF (the one that changed last quarter and nobody told you), copy the ICD-10 that justifies the CPT, paste it into a portal that logs you out every twenty minutes, and hit submit knowing there's maybe a 70% chance it comes back denied for a reason you could have caught if you'd had time to read all eleven pages of the policy.&lt;/p&gt;

&lt;p&gt;Then it comes back denied. And now it's an appeal, which is the whole thing again but angrier.&lt;/p&gt;

&lt;p&gt;If you've built anything near a medical practice, you know this queue never empties. It just ages. And every aged auth is revenue sitting in limbo. I spent the last year building software to work this pile, and the interesting part wasn't the AI — it was realizing prior auth isn't a &lt;em&gt;writing&lt;/em&gt; problem. It's a &lt;strong&gt;matching&lt;/strong&gt; problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pile is a matching problem, not a writing problem
&lt;/h2&gt;

&lt;p&gt;The naive build is: dump the chart into an LLM, say "write a medical necessity letter," ship it. It produces confident, fluent prose. It also gets denied, because a payer doesn't grade prose. A payer grades whether specific, enumerated criteria in &lt;em&gt;their&lt;/em&gt; coverage policy are each backed by &lt;em&gt;something in the chart&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So the real unit of work isn't a letter. It's a set of (criterion → evidence) pairs. Aetna's policy for a given procedure might require: failed conservative therapy for ≥6 weeks, documented functional impairment, imaging confirming the indication. UnitedHealthcare wants a different three things. The job is to bind each criterion to a concrete chart citation — and to notice, &lt;em&gt;before submitting&lt;/em&gt;, when a criterion has no evidence behind it. That empty binding is your denial risk, and you can see it coming.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assemble_justification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payer_policy&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;bindings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;criterion&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;payer_policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;required_criteria&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;evidence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;retrieve_evidence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;criterion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# dated chart citations
&lt;/span&gt;        &lt;span class="n"&gt;bindings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;criterion&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;criterion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;evidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;satisfied&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;unmet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bindings&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;satisfied&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="n"&gt;denial_risk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unmet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bindings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# the number you want BEFORE submit
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Justification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bindings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bindings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;risk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;denial_risk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gaps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;unmet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;gaps&lt;/code&gt; list is the whole product, honestly. It turns a blind submission into "these two criteria are unmet — go get the PT notes before you send this."&lt;/p&gt;

&lt;h2&gt;
  
  
  Grounding, because hallucinated evidence is malpractice
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;retrieve_evidence&lt;/code&gt; is where every shortcut bites you. You cannot let the model &lt;em&gt;say&lt;/em&gt; the conservative therapy happened. It has to &lt;em&gt;cite&lt;/em&gt; the note that says it, with a date. We treat any binding whose evidence isn't traceable to a real chart span as unsatisfied — a false "satisfied" is far more dangerous than a false gap. A gap gets flagged for a human; a fabricated citation gets you denied, or worse.&lt;/p&gt;

&lt;p&gt;The other subtle bit: coverage policies drift. The criteria set has to be re-parsed from the live policy, not memorized, or you'll assemble a beautiful justification against last year's rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  The denial is structured too
&lt;/h2&gt;

&lt;p&gt;When a denial comes back, resist the urge to "write an appeal." A denial cites specific reasons — usually a criterion the payer claims wasn't met. So the appeal is just the inverse operation: take each denial reason, find the criterion it maps to, and rebut it with the evidence binding you already built. Point by point, in the payer's own vocabulary. Half the time the evidence &lt;em&gt;was&lt;/em&gt; there and the reviewer missed it, and a targeted rebuttal is far stronger than a fresh persuasive essay.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually changed
&lt;/h2&gt;

&lt;p&gt;The lesson that generalizes beyond healthcare: when an LLM task feels like "generate a document," check whether it's secretly a &lt;em&gt;matching&lt;/em&gt; task with an audit trail. Model the criteria, bind them to evidence, expose the gaps, and let the fluent-prose part be the last, cheapest step. The pile still exists — but now something works it down instead of just aging it.&lt;/p&gt;

&lt;p&gt;We packaged this as &lt;strong&gt;Authorize&lt;/strong&gt; — &lt;a href="https://kynth.studio/l/authorize" rel="noopener noreferrer"&gt;try it free&lt;/a&gt;. It's how we built the thing that finally empties the queue.&lt;/p&gt;

</description>
      <category>healthcare</category>
      <category>ai</category>
      <category>automation</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Predicting When a Client Will Actually Pay: Modeling Invoice Timing With an AI Agent</title>
      <dc:creator>Kynth</dc:creator>
      <pubDate>Mon, 06 Jul 2026 00:38:49 +0000</pubDate>
      <link>https://dev.to/kynth/predicting-when-a-client-will-actually-pay-modeling-invoice-timing-with-an-ai-agent-2cjo</link>
      <guid>https://dev.to/kynth/predicting-when-a-client-will-actually-pay-modeling-invoice-timing-with-an-ai-agent-2cjo</guid>
      <description>&lt;p&gt;The single hardest thing about getting paid isn't writing the invoice. It's the follow-up — knowing &lt;em&gt;when&lt;/em&gt; to nudge a quiet client, and doing it in a tone that doesn't torch the relationship. Most tools solve this with a dumb cron job: "send a reminder 7 days after the due date." That's wrong for almost everyone, and here's why.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with fixed reminder schedules
&lt;/h2&gt;

&lt;p&gt;Payment behavior isn't uniform. One client pays like clockwork on day 32 of a "net 30" invoice — not late, just &lt;em&gt;their&lt;/em&gt; rhythm. Another pays on day 5 but only if you remind them on day 3. A blanket "day 7 past due" reminder annoys the first client (who was always going to pay) and misses the second (who needed the poke earlier).&lt;/p&gt;

&lt;p&gt;So the real problem is &lt;strong&gt;per-client timing prediction&lt;/strong&gt;, not scheduling. You want to model each client's payment distribution and act at the point where a reminder has the highest marginal effect — the moment they're most likely to convert intent into a transfer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modeling payment rhythm as a per-client distribution
&lt;/h2&gt;

&lt;p&gt;Every invoice gives you a labeled data point: &lt;code&gt;(sent_date, due_date, paid_date, amount, was_reminded)&lt;/code&gt;. Over time, per client, that's a distribution of "days from send to pay." The naive move is to average it. Don't — averages hide the shape, and the shape is the whole signal.&lt;/p&gt;

&lt;p&gt;We model each client's pay-day as a distribution and track two things that matter more than the mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dispersion&lt;/strong&gt; — a tight distribution (always day 30–32) means a reminder before day 30 is noise. A wide one means the client is reminder-sensitive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reminder lift&lt;/strong&gt; — comparing paid-day distributions with and without a nudge tells you whether reminders actually move this client, and by how much.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;paid_events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;              &lt;span class="c1"&gt;# list of days-to-pay
&lt;/span&gt;    &lt;span class="n"&gt;p50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p90&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;quantiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;lift&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days_without_reminder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days_with_reminder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# act just before the client's own habitual pay point,
&lt;/span&gt;    &lt;span class="c1"&gt;# but only if a nudge historically helps them
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lift&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MIN_LIFT_DAYS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;send_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;due_date&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;p50&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;REMINDER_LEAD&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;send_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;due_date&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;p90&lt;/span&gt;     &lt;span class="c1"&gt;# let reliable-but-slow payers be
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;else&lt;/code&gt; branch is the one people skip, and it's the most important. A client who reliably pays on day 45 doesn't need three reminders — they need zero until day 44. Suppressing unnecessary nudges is as valuable as sending well-timed ones, because every needless reminder trains the client to ignore you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cold start: the first invoice has no history
&lt;/h2&gt;

&lt;p&gt;With no per-client data, you fall back to a &lt;strong&gt;hierarchical prior&lt;/strong&gt;: start from the population distribution (or a segment — agencies pay differently than startups), then let each new payment pull that client's estimate toward their own behavior. Bayesian updating does this cleanly. After two or three invoices, the client-specific signal dominates the prior. Before that, you're at least not guessing blind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drafting in the user's voice — the calibration problem
&lt;/h2&gt;

&lt;p&gt;Timing gets the money moving; tone keeps the client. The reminder has to sound like &lt;em&gt;you&lt;/em&gt;, not like a collections agency. We feed an LLM a few of the user's real past messages as style exemplars, plus structured context (days overdue, prior reminders sent, relationship length) so the model can calibrate escalation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First nudge on a reliable client → light, assume-good-faith.&lt;/li&gt;
&lt;li&gt;Third nudge, 30 days over → firmer, still professional.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trick is that escalation is a &lt;em&gt;function of the client's own history&lt;/em&gt;, not a global template. Same overdue count, different tone, depending on whether this client has ghosted before. Pass that history into the prompt and the model calibrates it far better than any if/else ladder you'd hand-write.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes it an agent, not a script
&lt;/h2&gt;

&lt;p&gt;It closes the loop. Every paid invoice updates the distribution, re-estimates lift, and reschedules the &lt;em&gt;next&lt;/em&gt; client's reminders. It decides not just what to send but whether to send at all — and the "send nothing" decision is a first-class action. That feedback loop is the difference between automation and an agent that gets better at your specific book of clients over time.&lt;/p&gt;




&lt;p&gt;We built this as &lt;strong&gt;Tally&lt;/strong&gt; — it watches every invoice, learns each client's rhythm, and drafts reminders in your voice at the moment they actually pay. Try it free → &lt;a href="https://kynth.studio/l/tally" rel="noopener noreferrer"&gt;https://kynth.studio/l/tally&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>saas</category>
      <category>freelancing</category>
    </item>
  </channel>
</rss>
