<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Developer | VaultSafe | Chat with Your Files — AI Document Assistant</title><link>https://www.vaultsafe.ai/en/tag/developer/</link><atom:link href="https://www.vaultsafe.ai/en/tag/developer/index.xml" rel="self" type="application/rss+xml"/><description>Developer</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Sat, 08 Mar 2025 00:00:00 +0000</lastBuildDate><image><url>https://www.vaultsafe.ai/media/logo.svg</url><title>Developer</title><link>https://www.vaultsafe.ai/en/tag/developer/</link></image><item><title>How to Build Apps for VaultSafe</title><link>https://www.vaultsafe.ai/en/blog/build-apps-for-vaultsafe/</link><pubDate>Sat, 08 Mar 2025 00:00:00 +0000</pubDate><guid>https://www.vaultsafe.ai/en/blog/build-apps-for-vaultsafe/</guid><description>&lt;h1 id="how-to-build-apps-for-vaultsafe"&gt;How to Build Apps for VaultSafe&lt;/h1&gt;
&lt;p&gt;VaultSafe&amp;rsquo;s app ecosystem lets you build applications that run on document metadata—extracting birthdays, invoices, deadlines, or any structured data—and present it through configurable widgets. &lt;strong&gt;No code in the repo.&lt;/strong&gt; All app definitions live in the cloud. Here&amp;rsquo;s how it works and how to create your first app.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-pipeline"&gt;The Pipeline&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;User uploads a file&lt;/strong&gt; → VaultSafe&amp;rsquo;s AI analyzes it (document type, description, entities, key-value content).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Your app&amp;rsquo;s agent prompt runs&lt;/strong&gt; on that metadata → extracts structured data (e.g. birthdays, due dates).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data is stored&lt;/strong&gt; per user, per app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A widget renders&lt;/strong&gt; the data (list, table, or card view).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Critical:&lt;/strong&gt; Apps never see raw files. They only receive pre-extracted metadata. This keeps the system secure and fast.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="three-pieces-of-an-app"&gt;Three Pieces of an App&lt;/h2&gt;
&lt;h3 id="1-schema"&gt;1. Schema&lt;/h3&gt;
&lt;p&gt;Define what your app extracts. Example (Birthdays):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;extraction&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;birthdays&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;person_name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;date&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;file_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-agent-prompt"&gt;2. Agent Prompt&lt;/h3&gt;
&lt;p&gt;Instructions for the LLM. The input is JSON: &lt;code&gt;suggested_file_name&lt;/code&gt;, &lt;code&gt;type_of_file&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;main_person&lt;/code&gt;, &lt;code&gt;other_persons&lt;/code&gt;, &lt;code&gt;full_content&lt;/code&gt;, &lt;code&gt;file_id&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Birthday example:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You extract birthday-related information from document metadata. Only use information explicitly present in the input; do not infer or guess.&lt;/p&gt;
&lt;p&gt;Input is a JSON object with: suggested_file_name, type_of_file, description, main_person, other_persons, full_content (key-value from document).&lt;/p&gt;
&lt;p&gt;Return a JSON object with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;birthdays&amp;rdquo;: list of { &amp;ldquo;person_name&amp;rdquo;: string, &amp;ldquo;date&amp;rdquo;: string (YYYY-MM-DD or partial like &amp;ldquo;15 March&amp;rdquo;), &amp;ldquo;source&amp;rdquo;: string, &amp;ldquo;file_id&amp;rdquo;: string }&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If no birthday is explicitly present, return { &amp;ldquo;birthdays&amp;rdquo;: [] }. Do not fabricate dates.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="3-widget-config"&gt;3. Widget Config&lt;/h3&gt;
&lt;p&gt;How to display the data:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;type&lt;/td&gt;
&lt;td&gt;&lt;code&gt;list_by_date&lt;/code&gt;, &lt;code&gt;table&lt;/code&gt;, or &lt;code&gt;card&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;list_key&lt;/td&gt;
&lt;td&gt;&lt;code&gt;birthdays&lt;/code&gt; (key in extracted JSON)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sort_field&lt;/td&gt;
&lt;td&gt;&lt;code&gt;date&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;display_fields&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&amp;quot;person_name&amp;quot;, &amp;quot;date&amp;quot;, &amp;quot;source&amp;quot;]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;title&lt;/td&gt;
&lt;td&gt;&amp;ldquo;Birthdays&amp;rdquo;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;empty_message&lt;/td&gt;
&lt;td&gt;&amp;ldquo;No birthdays extracted yet. Upload documents&amp;hellip;&amp;rdquo;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="app-status-enable-for-all-vs-marketplace"&gt;App Status: Enable for All vs. Marketplace&lt;/h2&gt;
&lt;p&gt;When you publish an app, you choose:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Enable for all&lt;/strong&gt; — Automatically enabled for every user. Good for core features (e.g. Birthdays).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enabled by user&lt;/strong&gt; — App appears in the marketplace. Users browse and enable it themselves.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Users can &lt;strong&gt;disable any app&lt;/strong&gt; at any time.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="generic-widgets"&gt;Generic Widgets&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;list_by_date&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sorted list, ideal for dates (birthdays, deadlines)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;table&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tabular view, rows = items, columns = display_fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;card&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Card layout, one card per item&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;More widget types (tree, timeline, etc.) can be added over time.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="publishing"&gt;Publishing&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Use the VaultSafe Admin app (local only, never deployed).&lt;/li&gt;
&lt;li&gt;Create a new app with app_id, name, developer, agent_prompt, schema, widget.&lt;/li&gt;
&lt;li&gt;Set status: &lt;code&gt;enable_for_all&lt;/code&gt; or &lt;code&gt;enabled_by_user&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Save. If enable_for_all, a backfill runs to add the app for all users.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="user-data--rag"&gt;User Data &amp;amp; RAG&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Extracted data is stored in &lt;code&gt;user_app&lt;/code&gt; (user_id + app_id + data).&lt;/li&gt;
&lt;li&gt;Users can &lt;strong&gt;update&lt;/strong&gt; or &lt;strong&gt;delete&lt;/strong&gt; items in the Apps UI.&lt;/li&gt;
&lt;li&gt;App data is also embedded and stored in pgvector for LLM chat context. On update/delete, those chunks are kept in sync.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="full-example-birthdays-app"&gt;Full Example: Birthdays App&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;app_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;birthday&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Birthdays&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;developer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;vaultsafe&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;cost&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;price_display&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Free&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;enable_for_all&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;agent_prompt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;schema&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;extraction&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;birthdays&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;widget&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;list_by_date&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;list_key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;birthdays&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sort_field&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;date&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;display_fields&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;person_name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;date&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Birthdays&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;empty_message&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;No birthdays extracted yet.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="next-steps"&gt;Next Steps&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Read the
for full specs.&lt;/li&gt;
&lt;li&gt;Check
for the ecosystem overview.&lt;/li&gt;
&lt;li&gt;Contact
for the developer program.&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>