<?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: Nacho Coll</title>
    <description>The latest articles on DEV Community by Nacho Coll (@nachocoll).</description>
    <link>https://dev.to/nachocoll</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3817320%2Fec355ed8-2bb8-4983-ad03-6bb983f2b833.jpg</url>
      <title>DEV Community: Nacho Coll</title>
      <link>https://dev.to/nachocoll</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nachocoll"/>
    <language>en</language>
    <item>
      <title>Verifieerbare digitale badges uitgeven met BADGES.ninja: Nederlandse gids voor developers en e-learning platforms</title>
      <dc:creator>Nacho Coll</dc:creator>
      <pubDate>Mon, 27 Apr 2026 17:44:29 +0000</pubDate>
      <link>https://dev.to/nachocoll/verifieerbare-digitale-badges-uitgeven-met-badgesninja-nederlandse-gids-voor-developers-en-3c3k</link>
      <guid>https://dev.to/nachocoll/verifieerbare-digitale-badges-uitgeven-met-badgesninja-nederlandse-gids-voor-developers-en-3c3k</guid>
      <description>&lt;p&gt;De Nederlandse en Vlaamse markt voor online onderwijs en micro-credentials groeit snel. Platforms zoals Codaisseur, Winc Academy, NCOI, LOI, Coursera, Udemy en talloze aanbieders van beroepsopleidingen certificeren elk jaar duizenden lerenden — maar veel aanbieders sturen nog altijd een simpele PDF als bewijs van deelname.&lt;/p&gt;

&lt;p&gt;Een PDF is niet verifieerbaar, integreert slecht met LinkedIn, en overleeft zelden een wisseling van provider. &lt;strong&gt;BADGES.ninja&lt;/strong&gt; is een platform voor &lt;strong&gt;digitale certificaten volgens de Open Badge v2.0-standaard&lt;/strong&gt; dat precies dit probleem oplost — beschikbaar in het Nederlands en meer dan 30 andere talen.&lt;/p&gt;

&lt;p&gt;In deze gids laat ik zien hoe je certificaten uitgeeft via REST API, bulk-uitgifte via CSV integreert, en welke functies ik bijzonder interessant vind voor Nederlandstalige opleidingsaanbieders.&lt;/p&gt;

&lt;h2&gt;
  
  
  Waarom Open Badge v2.0?
&lt;/h2&gt;

&lt;p&gt;Open Badge v2.0 is de open standaard van IMS Global voor interoperabele digitale certificaten. In tegenstelling tot een PDF bevat elke Open Badge machine-leesbare metadata (uitgever, ontvanger, criteria, uitgiftedatum, vervaldatum, bewijs) en een hosting-URL waarmee elke derde partij het certificaat kan verifiëren. Het werkt precies als een webstandaard: een verifieerbaar JSON-document met ingebedde PNG- of SVG-grafiek.&lt;/p&gt;

&lt;p&gt;Het belangrijkste verschil: wanneer iemand de echtheid van een certificaat wil controleren, vraagt hij niet aan de ontvanger, maar aan de uitgever — cryptografisch.&lt;/p&gt;

&lt;h2&gt;
  
  
  De REST API in de praktijk
&lt;/h2&gt;

&lt;p&gt;BADGES.ninja biedt een volledige REST API met &lt;code&gt;X-Api-Key&lt;/code&gt;-authenticatie — ook in het &lt;strong&gt;gratis plan&lt;/strong&gt;. Dat is een duidelijk verschil met de meeste concurrenten, die programmatische toegang reserveren voor enterprise-tarieven.&lt;/p&gt;

&lt;p&gt;Voorbeeld: een nieuwe uitgever (Issuer) aanmaken:&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;-X&lt;/span&gt; POST https://api.badges.ninja/issuers &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: bws_your_api_key_here"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&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;'{
    "parameters": {
      "name": "Mijn Academie",
      "url": "https://example.com",
      "email": "admin@example.com"
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Antwoord:&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;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"info"&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;"issuerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.badges.ninja/certify-badge/issuer/abc123-def456"&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;De &lt;code&gt;issuerId&lt;/code&gt; is een permanente URL waarnaar verwezen wordt in de JSON-LD-context van het Open Badge-document. Net zo eenvoudig: een badge maken, een award (certificaat) uitgeven, een ontvanger op de hoogte stellen — alles via één enkele REST-endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bulk-uitgifte via CSV
&lt;/h2&gt;

&lt;p&gt;Wie ooit heeft geprobeerd om certificaten voor 2.000 bootcampafstudeerders één voor één uit te geven, kent het probleem. De &lt;code&gt;/awards/bulk&lt;/code&gt;-functie van BADGES.ninja accepteert een CSV-bestand met ontvangergegevens en regelt de rest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automatische retry bij fouten&lt;/li&gt;
&lt;li&gt;pauzeren/hervatten bij onderbreking&lt;/li&gt;
&lt;li&gt;e-mailmelding met individuele certificaatlink&lt;/li&gt;
&lt;li&gt;status-webhook voor elk uitgegeven badge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Precies het soort engineering-detail dat je waardeert wanneer het proces halverwege een verzending vastloopt.&lt;/p&gt;

&lt;h2&gt;
  
  
  IPFS-opslag: permanentie voor de afbeeldingen
&lt;/h2&gt;

&lt;p&gt;Een digitaal certificaat zou net zo lang moeten bestaan als een papieren diploma. Het probleem met klassieke oplossingen: badge-afbeeldingen worden gehost op traditionele servers en verdwijnen wanneer het bedrijf sluit of van provider wisselt.&lt;/p&gt;

&lt;p&gt;BADGES.ninja slaat de badge-grafieken op in &lt;strong&gt;IPFS&lt;/strong&gt; (gedecentraliseerd opslagsysteem). Zelfs als het platform op een dag wordt opgeheven, blijft de afbeelding via elke IPFS-gateway bereikbaar — via de content hash. Voor een hogeschool, een hbo-instelling of een publieke onderwijsinstelling is dat een belangrijk argument.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optionele blockchain-verankering
&lt;/h2&gt;

&lt;p&gt;Certificaten uit het Pro-plan kunnen optioneel verankerd worden op een publieke blockchain. Dat voegt een uitgever-onafhankelijke cryptografische verificatielaag toe — nuttig voor gevallen waarin verifieerbaarheid echt belangrijk is: gereguleerde beroepsdiploma's, compliance-certificaten, medische bijscholing, NL Qualifications-trajecten.&lt;/p&gt;

&lt;p&gt;Belangrijk: het is optioneel. Veel onderwijsaanbieders hebben de blockchain-laag niet nodig, en dat is helemaal prima.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ontvangerportaal en LinkedIn-integratie
&lt;/h2&gt;

&lt;p&gt;De waarde van een certificaat hangt af van hoe de ontvanger het gebruikt. BADGES.ninja bevat een &lt;strong&gt;wachtwoordloos ontvangerportaal&lt;/strong&gt; op &lt;code&gt;badges.ninja/me&lt;/code&gt;. Elke ontvanger verzamelt daar al zijn certificaten en kan een publiek profiel aanmaken op &lt;code&gt;badges.ninja/u/&amp;lt;alias&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Daarnaast bevat elke publieke certificaatpagina:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;een één-klik-knop &lt;strong&gt;"Toevoegen aan LinkedIn-profiel"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;een downloadbaar PDF-certificaat&lt;/li&gt;
&lt;li&gt;een scanbare QR-code voor papieren CV's&lt;/li&gt;
&lt;li&gt;een publieke verificatie-URL&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Meertaligheid als onderscheidende factor
&lt;/h2&gt;

&lt;p&gt;Vrijwel alle leidende platforms voor digitale certificaten zijn ontwikkeld in het Engels en vertalen in het beste geval alleen de gebruikersinterface. BADGES.ninja is vanaf het begin meertalig — meer dan &lt;strong&gt;30 talen&lt;/strong&gt; worden ondersteund, waaronder Nederlands. Zowel uitgever als ontvanger kunnen hun certificaten in hun eigen taal beheren en delen.&lt;/p&gt;

&lt;p&gt;Voor een Nederlandse hogeschool, een Vlaamse opleidingsinstelling, een Belgische universiteit of een EdTech-platform in de Benelux is dat geen esthetisch detail meer, maar een echte adoptie-eis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gratis plan om te starten
&lt;/h2&gt;

&lt;p&gt;Het gratis plan staat &lt;strong&gt;tot 100 uitgiftes per maand&lt;/strong&gt; toe — genoeg om het formaat te valideren of een kleine tech-community te certificeren, zonder creditcard. Een redelijke standaardlimiet die veel concurrenten niet bieden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusie
&lt;/h2&gt;

&lt;p&gt;Als jullie cursussen, bootcamps, bedrijfstrainingen, workshops of certificeringstrajecten verzorgen en jullie certificaatuitgifte willen moderniseren, nodig ik jullie uit om BADGES.ninja te proberen op &lt;a href="https://badges.ninja" rel="noopener noreferrer"&gt;https://badges.ninja&lt;/a&gt;. Het gratis plan dekt 100 uitgiftes per maand, zonder creditcard en zonder tijdslimiet.&lt;/p&gt;

&lt;p&gt;Vragen, feedback of use cases? Laat het weten in de reacties — ik antwoord graag. 🎓&lt;/p&gt;

</description>
      <category>dutch</category>
      <category>nederlands</category>
      <category>blockchain</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Emettere certificazioni digitali verificabili con BADGES.ninja: guida in italiano per sviluppatori e piattaforme e-learning</title>
      <dc:creator>Nacho Coll</dc:creator>
      <pubDate>Mon, 27 Apr 2026 17:41:35 +0000</pubDate>
      <link>https://dev.to/nachocoll/emettere-certificazioni-digitali-verificabili-con-badgesninja-guida-in-italiano-per-sviluppatori-3ace</link>
      <guid>https://dev.to/nachocoll/emettere-certificazioni-digitali-verificabili-con-badgesninja-guida-in-italiano-per-sviluppatori-3ace</guid>
      <description>&lt;p&gt;Il mercato italiano della formazione online e delle micro-credenziali sta crescendo rapidamente. Piattaforme come Udemy, Coursera, Talent Garden Innovation School, Aulab, Boolean, Edgemony e i numerosi enti di formazione professionale certificano ogni anno migliaia di studenti — ma molti continuano a inviare un semplice PDF come attestato di partecipazione.&lt;/p&gt;

&lt;p&gt;Un PDF non è verificabile, non si integra bene con LinkedIn e raramente sopravvive al cambio di provider. &lt;strong&gt;BADGES.ninja&lt;/strong&gt; è una piattaforma per &lt;strong&gt;certificazioni digitali secondo lo standard Open Badge v2.0&lt;/strong&gt; che risolve esattamente questo problema — disponibile nativamente in italiano e oltre 30 lingue.&lt;/p&gt;

&lt;p&gt;In questa guida mostro come emettere certificazioni via REST API, integrare l'emissione di massa via CSV e quali funzionalità trovo particolarmente interessanti per gli enti di formazione italiani.&lt;/p&gt;

&lt;h2&gt;
  
  
  Perché Open Badge v2.0?
&lt;/h2&gt;

&lt;p&gt;Open Badge v2.0 è lo standard aperto di IMS Global per le certificazioni digitali interoperabili. A differenza di un PDF, ogni Open Badge contiene metadati leggibili dalle macchine (emittente, destinatario, criteri, data di emissione, scadenza, prove) e un URL di hosting tramite cui qualsiasi terza parte può verificare il certificato. Funziona esattamente come uno standard web: un documento JSON verificabile con grafica PNG o SVG incorporata.&lt;/p&gt;

&lt;p&gt;La differenza fondamentale: quando qualcuno vuole verificare l'autenticità di una certificazione, non chiede al destinatario, ma all'emittente — crittograficamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  La REST API in pratica
&lt;/h2&gt;

&lt;p&gt;BADGES.ninja espone una REST API completa con autenticazione &lt;code&gt;X-Api-Key&lt;/code&gt; — anche nel &lt;strong&gt;piano gratuito&lt;/strong&gt;. È una differenza significativa rispetto alla maggior parte dei concorrenti, che riservano l'accesso programmatico ai piani enterprise.&lt;/p&gt;

&lt;p&gt;Esempio: creare un nuovo emittente (Issuer):&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;-X&lt;/span&gt; POST https://api.badges.ninja/issuers &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: bws_your_api_key_here"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&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;'{
    "parameters": {
      "name": "La Mia Accademia",
      "url": "https://example.com",
      "email": "admin@example.com"
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Risposta:&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;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"info"&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;"issuerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.badges.ninja/certify-badge/issuer/abc123-def456"&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;L'&lt;code&gt;issuerId&lt;/code&gt; è un URL permanente referenziato nel contesto JSON-LD del documento Open Badge. Allo stesso modo: creare un badge, emettere un award (certificazione), notificare un destinatario — tutto attraverso un singolo endpoint REST.&lt;/p&gt;

&lt;h2&gt;
  
  
  Emissione di massa via CSV
&lt;/h2&gt;

&lt;p&gt;Chiunque abbia provato a emettere certificazioni per 2.000 studenti uno per uno conosce il problema. La funzione &lt;code&gt;/awards/bulk&lt;/code&gt; di BADGES.ninja accetta un file CSV con i dati dei destinatari e gestisce il resto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ritento automatico in caso di errori&lt;/li&gt;
&lt;li&gt;pausa/ripresa in caso di interruzione&lt;/li&gt;
&lt;li&gt;notifica email con link individuale al certificato&lt;/li&gt;
&lt;li&gt;webhook di stato per ogni badge emesso&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esattamente il tipo di dettaglio ingegneristico che si apprezza quando il processo si interrompe a metà invio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Archiviazione su IPFS: permanenza per le immagini
&lt;/h2&gt;

&lt;p&gt;Una certificazione digitale dovrebbe durare quanto un diploma cartaceo. Il problema delle soluzioni classiche: le immagini dei badge sono ospitate su server tradizionali e scompaiono quando il provider chiude o cambia.&lt;/p&gt;

&lt;p&gt;BADGES.ninja archivia le grafiche dei badge su &lt;strong&gt;IPFS&lt;/strong&gt; (sistema di archiviazione decentralizzato). Anche se la piattaforma un giorno venisse dismessa, l'immagine resta accessibile tramite qualsiasi gateway IPFS — tramite il suo content hash. Per un'università, un ITS Academy o un ente pubblico di formazione, è un argomento di peso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ancoraggio blockchain opzionale
&lt;/h2&gt;

&lt;p&gt;Le certificazioni del piano Pro possono essere opzionalmente ancorate su una blockchain pubblica. Questo aggiunge un livello di verifica crittografica indipendente dall'emittente — utile per casi in cui la verificabilità è particolarmente importante: titoli professionali regolamentati, certificazioni di compliance, formazione medica continua, percorsi finanziati da Fondi Interprofessionali.&lt;/p&gt;

&lt;p&gt;Importante: è opzionale. Molti enti di formazione non hanno bisogno del livello blockchain, e va benissimo così.&lt;/p&gt;

&lt;h2&gt;
  
  
  Portale destinatario e integrazione LinkedIn
&lt;/h2&gt;

&lt;p&gt;Il valore di una certificazione dipende da come il destinatario la utilizza. BADGES.ninja include un &lt;strong&gt;portale destinatario senza password&lt;/strong&gt; all'indirizzo &lt;code&gt;badges.ninja/me&lt;/code&gt;. Ogni destinatario raccoglie qui tutte le sue certificazioni e può creare un profilo pubblico su &lt;code&gt;badges.ninja/u/&amp;lt;alias&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Inoltre, ogni pagina pubblica di certificazione include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;un pulsante in un clic &lt;strong&gt;"Aggiungi al profilo LinkedIn"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;un certificato PDF scaricabile&lt;/li&gt;
&lt;li&gt;un QR code scansionabile per CV cartacei&lt;/li&gt;
&lt;li&gt;un URL di verifica pubblica&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Multilinguismo come fattore differenziante
&lt;/h2&gt;

&lt;p&gt;Praticamente tutte le piattaforme leader per certificazioni digitali sono sviluppate in inglese e, nella migliore delle ipotesi, traducono solo l'interfaccia utente. BADGES.ninja è multilingue dall'origine — supporta oltre &lt;strong&gt;30 lingue&lt;/strong&gt;, tra cui italiano, spagnolo, francese, tedesco, portoghese e molte altre. Sia l'emittente che il destinatario possono gestire e condividere le certificazioni nella propria lingua.&lt;/p&gt;

&lt;p&gt;Per un'accademia italiana, un ITS, un'università svizzera italofona o una piattaforma EdTech del Sud Italia, questo non è un dettaglio estetico ma un vero requisito di adozione.&lt;/p&gt;

&lt;h2&gt;
  
  
  Piano gratuito per iniziare
&lt;/h2&gt;

&lt;p&gt;Il piano gratuito permette &lt;strong&gt;fino a 100 emissioni al mese&lt;/strong&gt; — sufficiente per validare il formato o certificare una piccola tech community senza carta di credito. Un limite ragionevole che molti concorrenti non offrono.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusione
&lt;/h2&gt;

&lt;p&gt;Se gestite corsi, bootcamp, formazione aziendale, workshop o percorsi certificati e volete modernizzare l'emissione delle certificazioni, vi invito a provare BADGES.ninja su &lt;a href="https://badges.ninja" rel="noopener noreferrer"&gt;https://badges.ninja&lt;/a&gt;. Il piano gratuito copre 100 emissioni al mese, senza carta di credito e senza scadenza temporale.&lt;/p&gt;

&lt;p&gt;Domande, feedback o casi d'uso? Lasciate un commento — rispondo volentieri. 🎓&lt;/p&gt;

</description>
      <category>italian</category>
      <category>italiano</category>
      <category>blockchain</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Verifizierbare digitale Zertifikate mit BADGES.ninja ausstellen: deutscher Leitfaden für Entwickler und Bildungsanbieter</title>
      <dc:creator>Nacho Coll</dc:creator>
      <pubDate>Mon, 27 Apr 2026 17:39:32 +0000</pubDate>
      <link>https://dev.to/nachocoll/verifizierbare-digitale-zertifikate-mit-badgesninja-ausstellen-deutscher-leitfaden-fur-entwickler-oim</link>
      <guid>https://dev.to/nachocoll/verifizierbare-digitale-zertifikate-mit-badgesninja-ausstellen-deutscher-leitfaden-fur-entwickler-oim</guid>
      <description>&lt;p&gt;Der deutsche Markt für Online-Weiterbildung und IHK-Zertifikatslehrgänge wächst stetig. Plattformen wie openHPI, Udemy, die IHK-Akademien, oncampus, OPEN vhb oder die zahlreichen Anbieter beruflicher Fortbildung qualifizieren jedes Jahr Hunderttausende Lernende — doch viele Anbieter verschicken nach wie vor ein einfaches PDF als Teilnahmenachweis.&lt;/p&gt;

&lt;p&gt;Ein PDF ist nicht überprüfbar, lässt sich nur schlecht in LinkedIn integrieren und überdauert kaum den nächsten Provider-Wechsel. &lt;strong&gt;BADGES.ninja&lt;/strong&gt; ist eine Plattform für &lt;strong&gt;digitale Zertifikate nach dem Open-Badge-v2.0-Standard&lt;/strong&gt;, die genau dieses Problem löst — und die nativ auf Deutsch (Deutschland und Österreich) zur Verfügung steht.&lt;/p&gt;

&lt;p&gt;In diesem Leitfaden zeige ich, wie man Zertifikate per REST-API ausstellt, eine CSV-Massenausstellung integriert und welche Funktionen ich für deutschsprachige Bildungsanbieter besonders interessant finde.&lt;/p&gt;

&lt;h2&gt;
  
  
  Warum Open Badge v2.0?
&lt;/h2&gt;

&lt;p&gt;Open Badge v2.0 ist der offene Standard von IMS Global für interoperable digitale Zertifikate. Im Gegensatz zu einem PDF enthält jedes Open Badge maschinenlesbare Metadaten (Aussteller, Empfänger, Kriterien, Ausstellungsdatum, Ablauf, Belege) und eine Hosting-URL, über die jeder Drittanbieter das Zertifikat verifizieren kann. Es funktioniert genau wie ein Webstandard: ein verifizierbares JSON-Dokument mit eingebetteter PNG- oder SVG-Grafik.&lt;/p&gt;

&lt;p&gt;Der wichtige Unterschied: Wenn jemand die Authentizität eines Zertifikats prüfen will, fragt er nicht den Empfänger, sondern den Aussteller — kryptografisch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Die REST-API in der Praxis
&lt;/h2&gt;

&lt;p&gt;BADGES.ninja stellt eine vollständige REST-API mit &lt;code&gt;X-Api-Key&lt;/code&gt;-Authentifizierung bereit — auch im &lt;strong&gt;kostenlosen Plan&lt;/strong&gt;. Das ist ein deutlicher Unterschied zu den meisten Konkurrenten, die programmatischen Zugriff den Enterprise-Tarifen vorbehalten.&lt;/p&gt;

&lt;p&gt;Beispiel: einen neuen Aussteller (Issuer) anlegen:&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;-X&lt;/span&gt; POST https://api.badges.ninja/issuers &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: bws_your_api_key_here"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&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;'{
    "parameters": {
      "name": "Meine Akademie",
      "url": "https://example.com",
      "email": "admin@example.com"
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Antwort:&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;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"info"&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;"issuerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.badges.ninja/certify-badge/issuer/abc123-def456"&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;Die &lt;code&gt;issuerId&lt;/code&gt; ist eine permanente URL, die im JSON-LD-Kontext des Open-Badge-Dokuments referenziert wird. Genauso einfach: ein Badge anlegen, ein Award (Zertifikat) ausstellen, einen Empfänger benachrichtigen — alles über einen einzigen REST-Endpunkt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Massenausstellung per CSV
&lt;/h2&gt;

&lt;p&gt;Wer einmal versucht hat, Zertifikate für 2.000 Bootcamp-Absolventen einzeln auszustellen, kennt das Problem. Die &lt;code&gt;/awards/bulk&lt;/code&gt;-Funktion in BADGES.ninja akzeptiert eine CSV-Datei mit Empfängerdaten und übernimmt den Rest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automatische Wiederholung bei Fehlern&lt;/li&gt;
&lt;li&gt;Pause/Wiederaufnahme bei Unterbrechung&lt;/li&gt;
&lt;li&gt;E-Mail-Benachrichtigung mit individuellem Zertifikatslink&lt;/li&gt;
&lt;li&gt;Status-Webhook für jedes ausgegebene Badge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Genau die Art Engineering-Detail, die man zu schätzen weiß, wenn der Prozess mitten im Versand abbricht.&lt;/p&gt;

&lt;h2&gt;
  
  
  IPFS-Speicherung: Permanenz für die Bildlinks
&lt;/h2&gt;

&lt;p&gt;Ein digitales Zertifikat sollte so lange bestehen wie ein Papierdiplom. Das Problem klassischer Lösungen: Die Badge-Bilder werden auf herkömmlichen Servern gehostet und verschwinden, sobald der Anbieter geschlossen oder gewechselt wird.&lt;/p&gt;

&lt;p&gt;BADGES.ninja speichert die Badge-Grafiken auf &lt;strong&gt;IPFS&lt;/strong&gt; (dezentrales Speichersystem). Selbst wenn die Plattform irgendwann eingestellt würde, bleibt das Bild über jeden IPFS-Gateway abrufbar — über seinen Content-Hash. Für eine Hochschule, eine IHK-Akademie oder eine öffentliche Bildungseinrichtung ein gewichtiges Argument.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optionale Blockchain-Verankerung
&lt;/h2&gt;

&lt;p&gt;Zertifikate aus dem Pro-Plan können optional auf einer öffentlichen Blockchain verankert werden. Das fügt eine vom Aussteller unabhängige kryptografische Verifizierungsschicht hinzu — sinnvoll für Fälle, in denen Verifizierbarkeit besonders wichtig ist: regulierte Berufsabschlüsse, Compliance-Zertifikate, medizinische Fortbildung, BAFA-geförderte Maßnahmen.&lt;/p&gt;

&lt;p&gt;Wichtig: Es ist optional. Viele Bildungsanbieter brauchen die Blockchain-Schicht nicht, und das ist völlig in Ordnung.&lt;/p&gt;

&lt;h2&gt;
  
  
  Empfängerportal und LinkedIn-Integration
&lt;/h2&gt;

&lt;p&gt;Der Wert eines Zertifikats hängt davon ab, wie der Empfänger es nutzt. BADGES.ninja bringt ein &lt;strong&gt;passwortloses Empfängerportal&lt;/strong&gt; unter &lt;code&gt;badges.ninja/me&lt;/code&gt; mit. Jeder Empfänger sammelt dort alle seine Zertifikate und kann ein öffentliches Profil unter &lt;code&gt;badges.ninja/u/&amp;lt;alias&amp;gt;&lt;/code&gt; anlegen.&lt;/p&gt;

&lt;p&gt;Zusätzlich enthält jede öffentliche Zertifikatsseite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;einen Ein-Klick-Button &lt;strong&gt;„Zu LinkedIn-Profil hinzufügen"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ein herunterladbares PDF-Zertifikat&lt;/li&gt;
&lt;li&gt;einen scanbaren QR-Code für gedruckte Lebensläufe&lt;/li&gt;
&lt;li&gt;eine öffentliche Verifizierungs-URL&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Mehrsprachigkeit als Differenzierungsmerkmal
&lt;/h2&gt;

&lt;p&gt;Praktisch alle führenden Plattformen für digitale Zertifikate sind in Englisch entwickelt und übersetzen bestenfalls die Benutzeroberfläche. BADGES.ninja ist von Anfang an mehrsprachig — über &lt;strong&gt;30 Sprachen&lt;/strong&gt; werden unterstützt, einschließlich Deutsch (Deutschland und Österreich). Sowohl Aussteller als auch Empfänger können ihre Zertifikate in der eigenen Sprache verwalten und teilen.&lt;/p&gt;

&lt;p&gt;Für eine deutsche IHK-Akademie, eine österreichische WIFI-Niederlassung, eine Schweizer Fachhochschule oder eine EdTech-Plattform in der DACH-Region ist das kein Detail, sondern eine echte Adoptionsanforderung.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kostenloser Plan zum Starten
&lt;/h2&gt;

&lt;p&gt;Der kostenlose Plan erlaubt &lt;strong&gt;bis zu 100 Ausstellungen pro Monat&lt;/strong&gt; — genug, um das Format zu validieren oder eine kleine Tech-Community zu zertifizieren, ohne Kreditkarte. Sinnvolle Standardgrenze, die viele Konkurrenten so nicht bieten.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fazit
&lt;/h2&gt;

&lt;p&gt;Wenn ihr Kurse, Bootcamps, betriebliche Schulungen, Workshops oder IHK-Zertifikatslehrgänge anbietet und eure Zertifikatsausgabe modernisieren möchtet, lade ich euch ein, BADGES.ninja unter &lt;a href="https://badges.ninja" rel="noopener noreferrer"&gt;https://badges.ninja&lt;/a&gt; auszuprobieren. Der kostenlose Plan deckt 100 Ausstellungen pro Monat ab, ohne Kreditkarte und ohne Zeitlimit.&lt;/p&gt;

&lt;p&gt;Fragen, Feedback oder Use Cases? Lasst es in den Kommentaren wissen — ich antworte gerne. 🎓&lt;/p&gt;

</description>
      <category>german</category>
      <category>deutsch</category>
      <category>blockchain</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Émettre des badges numériques vérifiables avec BADGES.ninja : guide en français pour développeurs et plateformes e-learning</title>
      <dc:creator>Nacho Coll</dc:creator>
      <pubDate>Sat, 25 Apr 2026 19:18:09 +0000</pubDate>
      <link>https://dev.to/nachocoll/emettre-des-badges-numeriques-verifiables-avec-badgesninja-guide-en-francais-pour-developpeurs-mp3</link>
      <guid>https://dev.to/nachocoll/emettre-des-badges-numeriques-verifiables-avec-badgesninja-guide-en-francais-pour-developpeurs-mp3</guid>
      <description>&lt;p&gt;Si vous développez une plateforme de cours en ligne, un LMS, un bootcamp ou tout produit qui délivre des « diplômes » à la fin d'un module, vous êtes probablement en train d'envoyer aujourd'hui un PDF par email. Ça fonctionne, mais ce n'est pas vérifiable, ça s'intègre mal avec LinkedIn, et ça ne résiste pas au temps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://badges.ninja" rel="noopener noreferrer"&gt;BADGES.ninja&lt;/a&gt; est une plateforme de credentials numériques conforme au standard &lt;strong&gt;Open Badge v2.0&lt;/strong&gt;, disponible dans plus de 30 langues — dont le français métropolitain et le français des Amériques — et qui expose une API REST complète dès le plan gratuit. Dans cet article, je passe en revue les 7 fonctionnalités qui comptent vraiment pour un profil technique qui va intégrer l'émission de credentials dans son produit.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. API REST complète avec &lt;code&gt;X-Api-Key&lt;/code&gt; sur tous les plans
&lt;/h2&gt;

&lt;p&gt;La différence la plus importante avec Credly ou Accredible : l'API REST est disponible &lt;strong&gt;sur le plan gratuit&lt;/strong&gt; et s'authentifie avec un simple en-tête &lt;code&gt;X-Api-Key&lt;/code&gt;. Vous générez une clé depuis le tableau de bord et vous émettez des credentials depuis votre backend en quelques minutes.&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;-X&lt;/span&gt; POST https://badges.ninja/api/awards &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: &lt;/span&gt;&lt;span class="nv"&gt;$BADGES_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&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;'{
    "badge_id": "abc123",
    "recipient_email": "alice@exemple.fr",    "recipient_name": "Alice Martin"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cela vous permet de valider l'intégration end-to-end &lt;strong&gt;avant&lt;/strong&gt; de prendre une décision d'achat — ce que les plateformes qui réservent l'API au plan entreprise ne permettent pas.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Émission en masse par CSV avec pause, reprise et réessai
&lt;/h2&gt;

&lt;p&gt;Quand un bootcamp termine une promotion de 500 personnes, émettre les credentials un par un n'est pas viable. La fonction &lt;strong&gt;bulk awards&lt;/strong&gt; accepte un CSV standard et traite le lot avec trois garanties : vous pouvez mettre en pause, reprendre exactement là où le processus s'est arrêté, et le système réessaye automatiquement les lignes en erreur transitoire. C'est le genre de détail qu'on apprécie quand un upload de 2 000 étudiants tombe au milieu.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Stockage permanent sur IPFS
&lt;/h2&gt;

&lt;p&gt;Les images des credentials sont stockées sur &lt;strong&gt;IPFS&lt;/strong&gt; (système de fichiers décentralisé), et non dans un bucket privé du fournisseur. Concrètement, même si BADGES.ninja cessait d'opérer un jour, les CIDs des images resteraient récupérables sur n'importe quelle gateway IPFS publique. Pour une université ou un organisme public qui délivre des titres avec une validité de long terme, c'est un argument fort par rapport à un hébergement centralisé traditionnel.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Ancrage blockchain optionnel (plan Pro)
&lt;/h2&gt;

&lt;p&gt;Les credentials du plan Pro peuvent être ancrés sur une &lt;strong&gt;blockchain publique&lt;/strong&gt; de manière optionnelle, ajoutant une couche de vérification cryptographique indépendante de l'émetteur. La page publique de chaque credential affiche le hash on-chain et un bouton de vérification. Ce n'est pas obligatoire — beaucoup de cas n'en ont pas besoin — mais c'est disponible quand le contexte réglementaire l'exige (formation médicale continue, certifications de conformité, titres professionnels réglementés comme ceux du RNCP).&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Éditeur visuel avec plus de 80 modèles
&lt;/h2&gt;

&lt;p&gt;Toutes les équipes n'ont pas un designer disponible pour créer des badges depuis zéro. L'éditeur de canvas inclut &lt;strong&gt;80+ modèles&lt;/strong&gt; (8 formes × 8 palettes), des filtres, des polices personnalisables, des icônes et des images personnelles. En 5 minutes vous avez l'identité visuelle d'une collection entière sans ouvrir Figma.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Portail du destinataire et bouton « Ajouter à LinkedIn »
&lt;/h2&gt;

&lt;p&gt;Un credential vaut ce qu'on en partage. Chaque destinataire reçoit un accès sans mot de passe à &lt;code&gt;badges.ninja/me&lt;/code&gt;, où il regroupe tous ses credentials et peut réclamer un profil public sur &lt;code&gt;badges.ninja/u/&amp;lt;alias&amp;gt;&lt;/code&gt;. Chaque page publique de credential inclut :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un bouton en un clic pour &lt;strong&gt;l'ajouter au profil LinkedIn&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Un &lt;strong&gt;certificat PDF&lt;/strong&gt; A4 téléchargeable&lt;/li&gt;
&lt;li&gt;Un &lt;strong&gt;QR code&lt;/strong&gt; scannable pour les CV imprimés&lt;/li&gt;
&lt;li&gt;Une URL publique de vérification&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Open Badge v2.0 + plan gratuit de 100 credentials/mois
&lt;/h2&gt;

&lt;p&gt;Open Badge v2.0 est le standard d'IMS Global pour les credentials interopérables. N'importe quel credential émis avec BADGES.ninja peut être lu par les backpacks tiers qui suivent ce même standard. Et le plan gratuit permet d'émettre jusqu'à &lt;strong&gt;100 credentials par mois&lt;/strong&gt; sans carte bancaire — largement assez pour valider l'intégration sur un cours complet avant de passer au Pro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Le facteur multilingue
&lt;/h2&gt;

&lt;p&gt;Un détail que les plateformes concurrentes mettent rarement en avant : BADGES.ninja est disponible dans &lt;strong&gt;plus de 30 langues&lt;/strong&gt;, regroupées par région (Amériques, Europe, Asie &amp;amp; Pacifique). Pour un organisme de formation français, une université belge, un bootcamp québécois ou une plateforme EdTech au Maghreb, cela signifie que le tableau de bord de l'émetteur &lt;strong&gt;et&lt;/strong&gt; le portail du destinataire fonctionnent dans la langue native de votre public. C'est un point de friction en moins pour l'adoption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Si vous construisez ou maintenez un produit e-learning en français, BADGES.ninja vaut le coup d'œil avant de choisir une plateforme anglo-saxonne plus chère. La combinaison API REST sur le plan gratuit, IPFS, blockchain optionnelle, émission en masse et support multilingue rend l'intégration rapide et la proposition de valeur côté destinataire solide.&lt;/p&gt;

&lt;p&gt;Commencez gratuitement sur 👉 &lt;a href="https://badges.ninja" rel="noopener noreferrer"&gt;https://badges.ninja&lt;/a&gt; — 100 émissions par mois sans carte bancaire.&lt;/p&gt;

</description>
      <category>french</category>
      <category>francais</category>
      <category>blockchain</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Emita credenciais digitais verificáveis com BADGES.ninja: guia em português para devs e plataformas EAD</title>
      <dc:creator>Nacho Coll</dc:creator>
      <pubDate>Sat, 25 Apr 2026 19:15:43 +0000</pubDate>
      <link>https://dev.to/nachocoll/emita-credenciais-digitais-verificaveis-com-badgesninja-guia-em-portugues-para-devs-e-plataformas-lkl</link>
      <guid>https://dev.to/nachocoll/emita-credenciais-digitais-verificaveis-com-badgesninja-guia-em-portugues-para-devs-e-plataformas-lkl</guid>
      <description>&lt;p&gt;Se você desenvolve uma plataforma de cursos online, um LMS, um bootcamp ou qualquer produto que entregue "diplomas" ao final de um módulo, provavelmente está gerando hoje um PDF e enviando por email. Funciona, mas não é verificável, não se integra bem com o LinkedIn e não resiste ao tempo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://badges.ninja" rel="noopener noreferrer"&gt;BADGES.ninja&lt;/a&gt; é uma plataforma de credenciais digitais conforme o padrão &lt;strong&gt;Open Badge v2.0&lt;/strong&gt; que está disponível em mais de 30 idiomas — incluindo português do Brasil e português de Portugal — e expõe uma REST API completa já no plano gratuito. Neste artigo, passo em revista os 7 recursos que mais pesam para um perfil técnico que vai integrar emissão de credenciais no seu produto.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. REST API completa com &lt;code&gt;X-Api-Key&lt;/code&gt; em todos os planos
&lt;/h2&gt;

&lt;p&gt;A diferença mais importante em relação a plataformas como Credly ou Accredible: a API REST está disponível no &lt;strong&gt;plano gratuito&lt;/strong&gt; e se autentica com um simples header &lt;code&gt;X-Api-Key&lt;/code&gt;. Você gera uma chave no painel e começa a emitir credenciais a partir do seu backend em minutos.&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;-X&lt;/span&gt; POST https://badges.ninja/api/awards &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: &lt;/span&gt;&lt;span class="nv"&gt;$BADGES_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&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;'{
    "badge_id": "abc123",
    "recipient_email": "aluno@exemplo.com",    "recipient_name": "João Silva"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso permite validar a integração end-to-end &lt;strong&gt;antes&lt;/strong&gt; de tomar uma decisão de compra, algo que plataformas com API somente no plano enterprise não permitem.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Emissão em massa por CSV com pausa, retomada e nova tentativa
&lt;/h2&gt;

&lt;p&gt;Quando um bootcamp encerra uma turma com 500 alunos, emitir uma credencial por vez não é viável. O recurso de &lt;strong&gt;bulk awards&lt;/strong&gt; aceita um CSV padrão e processa o lote com três garantias: você pode pausar, retomar exatamente de onde parou e o sistema tenta novamente automaticamente as linhas que tiveram erro transitório. É o tipo de detalhe que só se nota quando um upload de 2.000 alunos cai pela metade.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Armazenamento permanente em IPFS
&lt;/h2&gt;

&lt;p&gt;As imagens das credenciais são armazenadas em &lt;strong&gt;IPFS&lt;/strong&gt; (sistema de arquivos descentralizado), não em um bucket privado do fornecedor. Isso significa que mesmo se a BADGES.ninja deixasse de operar algum dia, os CIDs das imagens continuariam recuperáveis em qualquer gateway IPFS público. Para uma universidade ou um órgão público que emite títulos com validade de longo prazo, isso é um argumento forte frente a hospedagens centralizadas tradicionais.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Ancoragem opcional em blockchain (plano Pro)
&lt;/h2&gt;

&lt;p&gt;As credenciais do plano Pro podem ser ancoradas em uma &lt;strong&gt;blockchain pública&lt;/strong&gt; de forma opcional, adicionando uma camada de verificação criptográfica independente do emissor. A página pública de cada credencial mostra o hash on-chain e um botão de verificação. Não é obrigatório ativar — muitos casos não precisam — mas está disponível quando o contexto regulatório exige (formação médica continuada, certificações de compliance, títulos profissionais regulamentados).&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Editor visual com mais de 80 templates
&lt;/h2&gt;

&lt;p&gt;Nem todo time tem um designer disponível para criar insígnias do zero. O editor de canvas inclui &lt;strong&gt;80+ templates&lt;/strong&gt; (8 formas × 8 paletas), filtros, fontes personalizáveis, ícones e imagens próprias. Em 5 minutos você tem a identidade visual de uma coleção inteira sem abrir o Figma.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Portal do destinatário e botão "Adicionar ao LinkedIn"
&lt;/h2&gt;

&lt;p&gt;Uma credencial vale o quanto é compartilhada. Cada destinatário recebe acesso passwordless em &lt;code&gt;badges.ninja/me&lt;/code&gt;, onde reúne todas as suas credenciais e reivindica um perfil público em &lt;code&gt;badges.ninja/u/&amp;lt;alias&amp;gt;&lt;/code&gt;. Cada página pública de credencial inclui:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Botão de um toque para &lt;strong&gt;adicionar ao perfil do LinkedIn&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Certificado em PDF&lt;/strong&gt; A4 baixável&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QR Code&lt;/strong&gt; escaneável para currículos impressos&lt;/li&gt;
&lt;li&gt;URL pública de verificação&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Open Badge v2.0 + plano gratuito de 100 credenciais/mês
&lt;/h2&gt;

&lt;p&gt;Open Badge v2.0 é o padrão do IMS Global para credenciais interoperáveis. Qualquer credencial emitida com a BADGES.ninja pode ser lida por backpacks de terceiros que sigam o mesmo padrão. E o plano gratuito permite emitir até &lt;strong&gt;100 credenciais por mês&lt;/strong&gt; sem cartão de crédito — mais que suficiente para validar a integração com um curso completo antes de migrar para o Pro.&lt;/p&gt;

&lt;h2&gt;
  
  
  O fator multilíngue
&lt;/h2&gt;

&lt;p&gt;Um detalhe que as plataformas concorrentes raramente destacam: a BADGES.ninja está disponível em &lt;strong&gt;mais de 30 idiomas&lt;/strong&gt;, agrupados por região (Américas, Europa, Ásia &amp;amp; Pacífico). Para uma EdTech brasileira, uma faculdade portuguesa, um centro de formação profissional ou um bootcamp regional, isso significa que tanto o painel do emissor quanto o portal do destinatário podem funcionar no idioma nativo do seu público. É um ponto de fricção a menos na adoção.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Se você constrói ou mantém um produto EAD em português, vale a pena avaliar a BADGES.ninja antes de escolher uma plataforma anglo-saxã mais cara. A combinação de REST API no plano gratuito, IPFS, blockchain opcional, emissão em massa e suporte multilíngue torna a integração rápida e a proposta de valor para o destinatário sólida.&lt;/p&gt;

&lt;p&gt;Comece de graça em 👉 &lt;a href="https://badges.ninja" rel="noopener noreferrer"&gt;https://badges.ninja&lt;/a&gt; — 100 emissões por mês sem cartão de crédito.&lt;/p&gt;

</description>
      <category>portugues</category>
      <category>br</category>
      <category>blockchain</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Emite credenciales digitales verificables con BADGES.ninja: guía en español para desarrolladores y plataformas e-learning</title>
      <dc:creator>Nacho Coll</dc:creator>
      <pubDate>Sat, 25 Apr 2026 19:02:24 +0000</pubDate>
      <link>https://dev.to/nachocoll/emite-credenciales-digitales-verificables-con-badgesninja-guia-en-espanol-para-desarrolladores-y-74k</link>
      <guid>https://dev.to/nachocoll/emite-credenciales-digitales-verificables-con-badgesninja-guia-en-espanol-para-desarrolladores-y-74k</guid>
      <description>&lt;p&gt;Si tu equipo desarrolla una plataforma de cursos online, un LMS, un bootcamp o cualquier producto que entregue "diplomas" al finalizar un módulo, probablemente estés generando hoy un PDF y enviándolo por email. Funciona, pero no es verificable, no se integra con LinkedIn y no resiste el paso del tiempo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://badges.ninja" rel="noopener noreferrer"&gt;BADGES.ninja&lt;/a&gt; es una plataforma de credenciales digitales conforme al estándar &lt;strong&gt;Open Badge v2.0&lt;/strong&gt; que está disponible en más de 30 idiomas —incluyendo español de España, español de Estados Unidos, portugués de Brasil y catalán— y expone una REST API completa desde el plan gratuito. En este artículo paso revista a las 7 funciones que más peso tienen para un perfil técnico que vaya a integrar emisión de credenciales en su producto.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. REST API completa con &lt;code&gt;X-Api-Key&lt;/code&gt; en todos los planes
&lt;/h2&gt;

&lt;p&gt;La diferencia más importante con plataformas como Credly o Accredible: la API REST está disponible en el &lt;strong&gt;plan gratuito&lt;/strong&gt; y se autentica con una simple cabecera &lt;code&gt;X-Api-Key&lt;/code&gt;. Puedes generar una clave desde el panel y empezar a emitir credenciales desde tu backend en minutos.&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;-X&lt;/span&gt; POST https://badges.ninja/api/awards &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: &lt;/span&gt;&lt;span class="nv"&gt;$BADGES_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&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;'{
    "badge_id": "abc123",
    "recipient_email": "alumna@ejemplo.com",    "recipient_name": "María García"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto te permite validar la integración end-to-end &lt;strong&gt;antes&lt;/strong&gt; de tomar una decisión de compra, algo que las plataformas con API solo en plan empresarial no permiten.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Emisión masiva por CSV con pausa, reanudación y reintento
&lt;/h2&gt;

&lt;p&gt;Cuando un bootcamp termina un curso con 500 personas, la emisión una a una no es viable. La función de &lt;strong&gt;bulk awards&lt;/strong&gt; acepta un CSV estándar y procesa el lote con tres garantías: puedes pausar, reanudar exactamente donde se quedó y reintenta automáticamente las filas con errores transitorios. Es el tipo de detalle que solo se aprecia cuando el upload de 2.000 alumnos se cae a la mitad.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Almacenamiento permanente en IPFS
&lt;/h2&gt;

&lt;p&gt;Las imágenes de las credenciales se guardan en &lt;strong&gt;IPFS&lt;/strong&gt; (sistema de archivos descentralizado), no en un bucket privado del proveedor. Esto significa que aunque BADGES.ninja dejara de operar algún día, los CIDs de las imágenes seguirían siendo recuperables en cualquier gateway IPFS. Para una universidad o un organismo público que emite títulos con validez a largo plazo, esto es un argumento de peso frente a alojamiento centralizado tradicional.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Anclaje opcional en blockchain (plan Pro)
&lt;/h2&gt;

&lt;p&gt;Las credenciales del plan Pro pueden anclarse en una &lt;strong&gt;blockchain pública&lt;/strong&gt; de forma opcional, añadiendo una capa de verificación criptográfica independiente del emisor. La página pública de cada credencial muestra el hash on-chain y un botón de verificación. No es obligatorio activarlo —muchos casos no lo necesitan— pero está disponible cuando el contexto regulatorio lo exige (formación médica continuada, certificaciones de cumplimiento, títulos profesionales regulados).&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Diseñador visual con más de 80 plantillas
&lt;/h2&gt;

&lt;p&gt;No todos los equipos tienen un diseñador disponible para crear insignias desde cero. El editor de lienzo incluye &lt;strong&gt;80+ plantillas&lt;/strong&gt; (8 formas × 8 paletas), chips de filtrado, fuentes personalizables, íconos e imágenes propias. En 5 minutos tienes la identidad visual de toda una colección sin abrir Figma.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Portal del destinatario y botón "Añadir a LinkedIn"
&lt;/h2&gt;

&lt;p&gt;Una credencial vale lo que se comparte. Cada destinatario recibe acceso passwordless a &lt;code&gt;badges.ninja/me&lt;/code&gt;, donde reúne todas sus credenciales y reclama un perfil público en &lt;code&gt;badges.ninja/u/&amp;lt;alias&amp;gt;&lt;/code&gt;. Cada página pública de credencial incluye:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Botón de un toque para &lt;strong&gt;añadirla al perfil de LinkedIn&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Certificado en PDF&lt;/strong&gt; A4 descargable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Código QR&lt;/strong&gt; escaneable para CV impresos&lt;/li&gt;
&lt;li&gt;URL de verificación pública&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Open Badge v2.0 + plan gratuito de 100 credenciales/mes
&lt;/h2&gt;

&lt;p&gt;Open Badge v2.0 es el estándar de IMS Global para credenciales interoperables. Cualquier credencial emitida con BADGES.ninja se puede leer desde backpacks de terceros que sigan el mismo estándar. Y el plan gratuito permite emitir hasta &lt;strong&gt;100 credenciales al mes&lt;/strong&gt; sin tarjeta de crédito —más que suficiente para validar la integración con un curso completo antes de pasar al plan Pro.&lt;/p&gt;

&lt;h2&gt;
  
  
  El factor multilingüe
&lt;/h2&gt;

&lt;p&gt;Un detalle que no suelen destacar las plataformas competidoras: BADGES.ninja está disponible en &lt;strong&gt;más de 30 idiomas&lt;/strong&gt;, agrupados por región (Américas, Europa, Asia &amp;amp; Pacífico). Para una academia mexicana, un bootcamp argentino, una EdTech brasileña o un centro de formación profesional español, esto significa que tanto el panel del emisor como el portal del destinatario pueden funcionar en el idioma nativo de tu público. Es un punto de fricción menos en la adopción.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;Si estás construyendo o mantienes un producto e-learning en español, vale la pena evaluar BADGES.ninja antes de elegir una plataforma anglosajona más cara. La combinación de REST API en plan gratuito, IPFS, blockchain opcional, emisión masiva y soporte multilingüe hace que la integración sea rápida y la propuesta de valor para el destinatario sea sólida.&lt;/p&gt;

&lt;p&gt;Empieza gratis en 👉 &lt;a href="https://badges.ninja" rel="noopener noreferrer"&gt;https://badges.ninja&lt;/a&gt; — 100 otorgamientos al mes sin tarjeta de crédito.&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>espanol</category>
      <category>blockchain</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Pinea archivos en IPFS desde Claude Code, Cursor y Windsurf (Servidor MCP)</title>
      <dc:creator>Nacho Coll</dc:creator>
      <pubDate>Sat, 25 Apr 2026 10:47:59 +0000</pubDate>
      <link>https://dev.to/nachocoll/pinea-archivos-en-ipfs-desde-claude-code-cursor-y-windsurf-servidor-mcp-4e73</link>
      <guid>https://dev.to/nachocoll/pinea-archivos-en-ipfs-desde-claude-code-cursor-y-windsurf-servidor-mcp-4e73</guid>
      <description>&lt;p&gt;¡Hola, devs! Soy Nacho, parte del equipo de &lt;strong&gt;BWS (Blockchain Web Services)&lt;/strong&gt;. Acabamos de lanzar &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;IPFS.NINJA&lt;/a&gt;&lt;/strong&gt;, un servicio gestionado de pinning para IPFS, y una de las integraciones que más uso personalmente es el &lt;strong&gt;servidor MCP&lt;/strong&gt; que te permite subir archivos, pinear CIDs y consultar uso de almacenamiento &lt;strong&gt;directamente desde Claude Code, Cursor o Windsurf&lt;/strong&gt; — simplemente hablando con la IA en español.&lt;/p&gt;

&lt;p&gt;Disclosure completo: trabajo en este producto. Este post es un walkthrough transparente del equipo que lo construyó.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué te ofrece MCP
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol (MCP)&lt;/a&gt; es un estándar abierto para conectar asistentes de IA con herramientas externas. Nuestro servidor MCP expone &lt;strong&gt;12 tools&lt;/strong&gt; que el modelo puede invocar a mitad de conversación contra tu cuenta IPFS.NINJA:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operaciones de archivo&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ipfs_upload&lt;/code&gt; — Subir contenido (base64 o texto)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_upload_json&lt;/code&gt; — Subir un objeto JSON&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_import_car&lt;/code&gt; — Importar archivo CAR (DAG import)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_list&lt;/code&gt; — Listar tus archivos subidos&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_get&lt;/code&gt; — Obtener metadatos por CID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_delete&lt;/code&gt; — Despinear y borrar un archivo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pinning&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ipfs_pin&lt;/code&gt; — Pinear un CID existente de la red&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_pin_status&lt;/code&gt; — Comprobar el progreso del pin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Organización&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ipfs_folders_list&lt;/code&gt; / &lt;code&gt;ipfs_folders_create&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cuenta&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ipfs_profile&lt;/code&gt; — Plan, almacenamiento, ancho de banda&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_analytics&lt;/code&gt; — Estadísticas diarias&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El efecto práctico: dejas de saltar entre terminal, dashboard y editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup en Claude Code (60 segundos)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Regístrate en &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;ipfs.ninja&lt;/a&gt;&lt;/strong&gt; (gratis) y crea una API key en Dashboard → API Keys. Cópiala (sólo se muestra una vez).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Añade el servidor MCP:&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 ipfs-ninja &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--transport&lt;/span&gt; stdio &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;IPFS_NINJA_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bws_tu_api_key_completa_aqui &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--&lt;/span&gt; npx &lt;span class="nt"&gt;-y&lt;/span&gt; @ipfs-ninja/mcp-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O añádelo manualmente a tu &lt;code&gt;.claude/settings.json&lt;/code&gt;:&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;"mcpServers"&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;"ipfs-ninja"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@ipfs-ninja/mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;"IPFS_NINJA_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bws_tu_api_key_completa_aqui"&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;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;strong&gt;3.&lt;/strong&gt; Reinicia Claude Code. Escribe &lt;code&gt;/mcp&lt;/code&gt; para confirmar que &lt;code&gt;ipfs-ninja&lt;/code&gt; está conectado.&lt;/p&gt;

&lt;p&gt;El paquete npm es &lt;code&gt;@ipfs-ninja/mcp-server&lt;/code&gt; — sin instalación global, se ejecuta vía &lt;code&gt;npx&lt;/code&gt;. Requiere Node.js 18+.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup en Cursor / Windsurf
&lt;/h2&gt;

&lt;p&gt;En Settings → MCP Servers, añade:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ipfs-ninja&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transport&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stdio&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Args&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-y @ipfs-ninja/mcp-server&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IPFS_NINJA_API_KEY=bws_...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Cómo se siente en la práctica
&lt;/h2&gt;

&lt;p&gt;Una vez instalado, simplemente le hablas al asistente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tú: Sube mi README.md a IPFS
Tú: Lista mis archivos recientes
Tú: ¿Cuánto almacenamiento estoy usando?
Tú: Pinea bafyabc123... desde la red IPFS
Tú: Crea una carpeta llamada "project-assets"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El modelo elige la herramienta correcta, llama a nuestra API y devuelve un CID + una URL pública de gateway tipo &lt;code&gt;https://ipfs.ninja/ipfs/&amp;lt;CID&amp;gt;&lt;/code&gt;. Sin copiar comandos curl, sin cambiar de ventana.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflows reales que esto desbloquea
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Deploy de un sitio estático a IPFS desde Claude Code:&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;Tú: Sube el contenido de mi carpeta dist/ a IPFS
Claude: [sube cada archivo, devuelve CIDs]
Tú: ¿Cuál es el CID de index.html?
Claude: [llama a ipfs_get] → QmXyz... — https://ipfs.ninja/ipfs/QmXyz...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pipeline de metadata para NFTs:&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;Tú: Crea una carpeta "my-collection" y sube este JSON de metadata
Claude: [llama a ipfs_folders_create, luego ipfs_upload_json]
        → Carpeta: my-collection
        → CID: QmAbc... — URL permanente lista para tu smart contract
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Monitorizar uso sin salir del editor:&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;Tú: ¿Estoy cerca del límite de almacenamiento?
Claude: [llama a ipfs_profile]
        → Plan: Bodhi, Almacenamiento: 45.2 MB / 100 GB (0.04%)
Tú: Muéstrame mi ancho de banda esta semana
Claude: [llama a ipfs_analytics con days=7]
        → 2.3 MB de banda, 45 requests en 3 días
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pinear contenido existente de la red:&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;Tú: Pinea el readme de IPFS en QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG
Claude: [llama a ipfs_pin] → ¡Pin iniciado! Estado: pinning
Tú: ¿Ya terminó?
Claude: [llama a ipfs_pin_status] → Estado: pinned, Tamaño: 0.008 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting (los tres problemas que de verdad ocurren)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;IPFS_NINJA_API_KEY environment variable is required&lt;/code&gt;&lt;/strong&gt; — al bloque &lt;code&gt;env&lt;/code&gt; de tu MCP config le falta la clave.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;API error 402: not enough storage&lt;/code&gt;&lt;/strong&gt; — has alcanzado el límite de almacenamiento del plan. Haz upgrade o borra archivos no usados.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El servidor no aparece en &lt;code&gt;/mcp&lt;/code&gt;&lt;/strong&gt; — olvidaste reiniciar el editor tras añadirlo. Comprueba también que &lt;code&gt;node --version&lt;/code&gt; ≥ 18.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pruébalo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Documentación del MCP Server (en español): &lt;strong&gt;&lt;a href="https://ipfs.ninja/docs/es/api/mcp-server" rel="noopener noreferrer"&gt;ipfs.ninja/docs/es/api/mcp-server&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Paquete npm: &lt;code&gt;@ipfs-ninja/mcp-server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Regístrate gratis: &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;ipfs.ninja&lt;/a&gt;&lt;/strong&gt; (Plan Dharma: 1 GB de almacenamiento, 5 GB de banda/mes, todas las features)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si construyes algo interesante con esto (auto-pinning de assets de blog en cada commit, flujos de mint de NFT desde la IA, etc.), me encantaría que lo cuentes en los comentarios. Lo leemos todo.&lt;/p&gt;

&lt;p&gt;— Nacho, equipo BWS&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>showdev</category>
      <category>spanish</category>
      <category>web3</category>
    </item>
    <item>
      <title>Faça pin de arquivos no IPFS direto do Claude Code, Cursor e Windsurf (Servidor MCP)</title>
      <dc:creator>Nacho Coll</dc:creator>
      <pubDate>Sat, 25 Apr 2026 10:39:12 +0000</pubDate>
      <link>https://dev.to/nachocoll/faca-pin-de-arquivos-no-ipfs-direto-do-claude-code-cursor-e-windsurf-servidor-mcp-39pf</link>
      <guid>https://dev.to/nachocoll/faca-pin-de-arquivos-no-ipfs-direto-do-claude-code-cursor-e-windsurf-servidor-mcp-39pf</guid>
      <description>&lt;p&gt;E aí, devs — sou o Nacho, parte do time da &lt;strong&gt;BWS (Blockchain Web Services)&lt;/strong&gt;. Acabamos de lançar o &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;IPFS.NINJA&lt;/a&gt;&lt;/strong&gt;, um serviço gerenciado de pinning para IPFS, e uma das integrações que eu pessoalmente mais uso é o &lt;strong&gt;servidor MCP&lt;/strong&gt; que permite fazer upload de arquivos, pin de CIDs e consultar uso de armazenamento &lt;strong&gt;direto do Claude Code, Cursor ou Windsurf&lt;/strong&gt; — só conversando com a IA em portuguê.&lt;/p&gt;

&lt;p&gt;Disclosure completo: eu trabalho neste produto. Este post é um walkthrough transparente do time que construíu a ferramenta.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que o MCP entrega
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol (MCP)&lt;/a&gt; é um padrão aberto para conectar assistentes de IA a ferramentas externas. Nosso servidor MCP expõe &lt;strong&gt;12 tools&lt;/strong&gt; que o modelo pode chamar no meio da conversa contra sua conta IPFS.NINJA:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operações de arquivo&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ipfs_upload&lt;/code&gt; — Upload de conteúdo (base64 ou texto)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_upload_json&lt;/code&gt; — Upload de objeto JSON&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_import_car&lt;/code&gt; — Importar CAR file (DAG import)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_list&lt;/code&gt; — Listar arquivos enviados&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_get&lt;/code&gt; — Metadados de arquivo por CID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_delete&lt;/code&gt; — Despinar e deletar um arquivo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pinning&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ipfs_pin&lt;/code&gt; — Pinar um CID existente da rede&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_pin_status&lt;/code&gt; — Verificar progresso do pin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Organização&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ipfs_folders_list&lt;/code&gt; / &lt;code&gt;ipfs_folders_create&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conta&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ipfs_profile&lt;/code&gt; — Plano, armazenamento, banda&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_analytics&lt;/code&gt; — Estatísticas diárias&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Resultado prático: você para de alternar entre terminal, dashboard e editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup no Claude Code (60 segundos)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Crie sua conta em &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;ipfs.ninja&lt;/a&gt;&lt;/strong&gt; (grátis) e gere uma API key em Dashboard → API Keys. Copie a chave (é mostrada uma vez só).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Adicione o servidor MCP:&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 ipfs-ninja &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--transport&lt;/span&gt; stdio &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;IPFS_NINJA_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bws_sua_api_key_completa_aqui &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--&lt;/span&gt; npx &lt;span class="nt"&gt;-y&lt;/span&gt; @ipfs-ninja/mcp-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ou adicione manualmente no &lt;code&gt;.claude/settings.json&lt;/code&gt;:&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;"mcpServers"&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;"ipfs-ninja"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@ipfs-ninja/mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;"IPFS_NINJA_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bws_sua_api_key_completa_aqui"&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;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;strong&gt;3.&lt;/strong&gt; Reinicie o Claude Code. Digite &lt;code&gt;/mcp&lt;/code&gt; para confirmar que &lt;code&gt;ipfs-ninja&lt;/code&gt; está conectado.&lt;/p&gt;

&lt;p&gt;O pacote npm é &lt;code&gt;@ipfs-ninja/mcp-server&lt;/code&gt; — sem necessidade de instalação global, roda via &lt;code&gt;npx&lt;/code&gt;. Requer Node.js 18+.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup no Cursor / Windsurf
&lt;/h2&gt;

&lt;p&gt;Em Settings → MCP Servers, adicione:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ipfs-ninja&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transport&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stdio&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Args&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-y @ipfs-ninja/mcp-server&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IPFS_NINJA_API_KEY=bws_...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Como é na prática
&lt;/h2&gt;

&lt;p&gt;Depois de instalado, você só conversa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Você: Faz upload do meu README.md para o IPFS
Você: Lista meus arquivos recentes
Você: Quanto storage estou usando?
Você: Pina o CID bafyabc123... da rede IPFS
Você: Cria uma pasta chamada "project-assets"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O modelo escolhe a tool certa, chama nossa API, e retorna um CID + uma URL pública de gateway tipo &lt;code&gt;https://ipfs.ninja/ipfs/&amp;lt;CID&amp;gt;&lt;/code&gt;. Sem ficar copiando comando curl, sem trocar de janela.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflows reais que isso destrava
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Deploy de site estático no IPFS direto do Claude Code:&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;Você: Faz upload do conteúdo da minha pasta dist/ para o IPFS
Claude: [faz upload de cada arquivo, retorna CIDs]
Você: Qual o CID do index.html?
Claude: [chama ipfs_get] → QmXyz... — https://ipfs.ninja/ipfs/QmXyz...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pipeline de metadados de NFT:&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;Você: Cria uma pasta "my-collection" e faz upload deste JSON de metadata
Claude: [chama ipfs_folders_create, depois ipfs_upload_json]
        → Pasta: my-collection
        → CID: QmAbc... — URL permanente pronta para o smart contract
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Monitorar uso sem sair do editor:&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;Você: Tô perto do meu limite de storage?
Claude: [chama ipfs_profile]
        → Plano: Bodhi, Storage: 45.2 MB / 100 GB (0.04%)
Você: Mostra minha banda essa semana
Claude: [chama ipfs_analytics com days=7]
        → 2.3 MB de banda, 45 requests em 3 dias
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pinar conteúdo existente da rede:&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;Você: Pina o readme do IPFS em QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG
Claude: [chama ipfs_pin] → Pin iniciado! Status: pinning
Você: Já acabou?
Claude: [chama ipfs_pin_status] → Status: pinned, Tamanho: 0.008 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting (os três problemas que de fato acontecem)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;IPFS_NINJA_API_KEY environment variable is required&lt;/code&gt;&lt;/strong&gt; — o bloco &lt;code&gt;env&lt;/code&gt; do MCP config está sem a chave.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;API error 402: not enough storage&lt;/code&gt;&lt;/strong&gt; — você atingiu o limite do plano. Faz upgrade ou apaga arquivos não usados.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Servidor não aparece no &lt;code&gt;/mcp&lt;/code&gt;&lt;/strong&gt; — esqueceu de reiniciar o editor depois de adicionar. Confirma também que &lt;code&gt;node --version&lt;/code&gt; ≥ 18.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Experimenta
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Documentação do MCP Server (em português): &lt;strong&gt;&lt;a href="https://ipfs.ninja/docs/pt-BR/api/mcp-server" rel="noopener noreferrer"&gt;ipfs.ninja/docs/pt-BR/api/mcp-server&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Pacote npm: &lt;code&gt;@ipfs-ninja/mcp-server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Cadastre-se grátis: &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;ipfs.ninja&lt;/a&gt;&lt;/strong&gt; (Plano Dharma: 1 GB de storage, 5 GB de banda/mês, todos os recursos)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se você construir algo legal com isso (auto-pinning de assets de blog no commit, fluxo de mint de NFT direto da IA, etc.), conta nos comentários. A gente lê tudo.&lt;/p&gt;

&lt;p&gt;— Nacho, time BWS&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>showdev</category>
      <category>web3</category>
    </item>
    <item>
      <title>Mutable Content on IPFS: A Practical Guide to IPNS (with Real Examples)</title>
      <dc:creator>Nacho Coll</dc:creator>
      <pubDate>Sat, 25 Apr 2026 10:13:42 +0000</pubDate>
      <link>https://dev.to/nachocoll/mutable-content-on-ipfs-a-practical-guide-to-ipns-with-real-examples-dm3</link>
      <guid>https://dev.to/nachocoll/mutable-content-on-ipfs-a-practical-guide-to-ipns-with-real-examples-dm3</guid>
      <description>&lt;p&gt;Hi devs — Nacho here from the &lt;strong&gt;BWS (Blockchain Web Services)&lt;/strong&gt; team. We just shipped &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;IPFS.NINJA&lt;/a&gt;&lt;/strong&gt;, a managed IPFS pinning service, and one of the most common questions we get is: &lt;em&gt;“How do I update content on IPFS without changing the URL?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The answer is &lt;strong&gt;IPNS (InterPlanetary Name System)&lt;/strong&gt; — and it's one of those features that sounds confusing until you see it in action.&lt;/p&gt;

&lt;p&gt;Full disclosure: I work on this product. This post is a transparent walkthrough from the team that built it, with concrete examples you can copy-paste.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem IPNS solves
&lt;/h2&gt;

&lt;p&gt;IPFS is content-addressed: change a single byte in a file, and the CID changes. That's a feature — immutability and verifiability are the whole point.&lt;/p&gt;

&lt;p&gt;But it's also a problem when you want to update something:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your dApp config evolves → new CID → every client needs the new link.&lt;/li&gt;
&lt;li&gt;Your NFT metadata levels up → new CID → your smart contract's &lt;code&gt;tokenURI&lt;/code&gt; is now pointing at stale content.&lt;/li&gt;
&lt;li&gt;You redeploy your IPFS-hosted website → new CID → you have to tell users the new URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;IPNS gives you a &lt;strong&gt;stable, shareable address that you control&lt;/strong&gt; and can re-point at any time.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works (the short version)
&lt;/h2&gt;

&lt;p&gt;When you create an IPNS name, an Ed25519 keypair is generated. The hash of the public key becomes your IPNS address (a string starting with &lt;code&gt;k51...&lt;/code&gt;). To publish, you sign a record saying &lt;em&gt;“this name points to CID X”&lt;/em&gt; and broadcast it to the IPFS DHT. To resolve, anyone can query the DHT for the latest signed record.&lt;/p&gt;

&lt;p&gt;Key properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stable&lt;/strong&gt;: the &lt;code&gt;k51...&lt;/code&gt; address never changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutable&lt;/strong&gt;: you can re-publish to point it at a new CID anytime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Owned&lt;/strong&gt;: only the holder of the private key (you) can update it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decentralized&lt;/strong&gt;: any IPFS gateway can resolve it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use cases that actually matter
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Websites on IPFS&lt;/strong&gt; — redeploy gives a new CID, but &lt;code&gt;ipns://k51...&lt;/code&gt; always serves the latest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NFT metadata that evolves&lt;/strong&gt; — set &lt;code&gt;tokenURI = ipns://k51...&lt;/code&gt; once; update metadata as the asset evolves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App config files&lt;/strong&gt; — update without redeploying the app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data feeds / daily datasets&lt;/strong&gt; — publish under a stable address consumers can poll.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNSLink&lt;/strong&gt; — connect a real domain (&lt;code&gt;yourdomain.com&lt;/code&gt;) to an IPNS name.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting up an IPNS name on IPFS.NINJA
&lt;/h2&gt;

&lt;p&gt;IPNS is included on the &lt;strong&gt;Bodhi ($5/mo)&lt;/strong&gt; and &lt;strong&gt;Nirvana ($29/mo)&lt;/strong&gt; plans (3 names / 100 publishes per month, and 10 names / 1,000 publishes respectively). Records are auto-republished every 12 hours so they stay alive on the network.&lt;/p&gt;

&lt;p&gt;From the dashboard:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Hosting → IPNS&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create name&lt;/strong&gt;, give it a label (e.g. &lt;code&gt;my-website&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Copy the resulting &lt;code&gt;k51...&lt;/code&gt; address — this is your permanent shareable handle.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Publish&lt;/strong&gt;, paste the CID you want it to point to, and you're live.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Or from the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create the IPNS key&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.ipfs.ninja/ipns/keys &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: bws_your_api_key_here"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&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;'{"name": "my-website"}'&lt;/span&gt;

&lt;span class="c"&gt;# Publish a CID to it&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.ipfs.ninja/ipns/publish &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: bws_your_api_key_here"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&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;'{"ipnsName": "k51qzi5uqu5dlvj2bv6...", "cid": "bafybei..."}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resolve from any gateway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://ipfs.ninja/ipns/k51qzi5uqu5dlvj2bv6...
https://dweb.link/ipns/k51qzi5uqu5dlvj2bv6...
ipns://k51qzi5uqu5dlvj2bv6...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example 1: Static site deployments
&lt;/h2&gt;

&lt;p&gt;A tiny CI script that uploads &lt;code&gt;dist/&lt;/code&gt; and updates IPNS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Upload build output&lt;/span&gt;
&lt;span class="nv"&gt;CID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.ipfs.ninja/upload/new &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: &lt;/span&gt;&lt;span class="nv"&gt;$IPFS_NINJA_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;content&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;dist/index.html | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-w0&lt;/span&gt; | jq &lt;span class="nt"&gt;-Rs&lt;/span&gt; .&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Website v2.1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.cid'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Re-point IPNS&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.ipfs.ninja/ipns/publish &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: &lt;/span&gt;&lt;span class="nv"&gt;$IPFS_NINJA_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;ipnsName&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;k51qzi5uqu5dlvj2bv6...&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;cid&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$CID&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In GitHub Actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload to IPFS and publish IPNS&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;CID=$(curl -s -X POST https://api.ipfs.ninja/upload/new \&lt;/span&gt;
      &lt;span class="s"&gt;-H "X-Api-Key: ${{ secrets.IPFS_NINJA_API_KEY }}" \&lt;/span&gt;
      &lt;span class="s"&gt;-H "Content-Type: application/json" \&lt;/span&gt;
      &lt;span class="s"&gt;-d '{"content": '"$(cat build/output.json)"', "description": "Deploy ${{ github.sha }}"}' \&lt;/span&gt;
      &lt;span class="s"&gt;| jq -r '.cid')&lt;/span&gt;
    &lt;span class="s"&gt;curl -X POST https://api.ipfs.ninja/ipns/publish \&lt;/span&gt;
      &lt;span class="s"&gt;-H "X-Api-Key: ${{ secrets.IPFS_NINJA_API_KEY }}" \&lt;/span&gt;
      &lt;span class="s"&gt;-H "Content-Type: application/json" \&lt;/span&gt;
      &lt;span class="s"&gt;-d '{"ipnsName": "${{ vars.IPNS_NAME }}", "cid": "'"$CID"'"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your users always hit &lt;code&gt;ipns://k51...&lt;/code&gt; (or your DNSLink domain). The deployment URL never changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 2: Mutable NFT metadata
&lt;/h2&gt;

&lt;p&gt;This is the killer use case for game items, evolving art, dynamic profiles — anywhere on-chain assets need to change without redeploying the contract.&lt;/p&gt;

&lt;p&gt;Your smart contract sets &lt;code&gt;tokenURI&lt;/code&gt; to an IPNS address once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;tokenURI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ipns://k51qzi5uqu5dlvj2bv6...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the NFT evolves (e.g. game item levels up):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newMetadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dragon Sword&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A legendary weapon — Level 5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ipfs://QmNewImageCID...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;attributes&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 class="na"&gt;trait_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;Level&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&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="na"&gt;trait_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;Damage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&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 class="c1"&gt;// 1. Upload the new metadata&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uploadRes&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;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.ipfs.ninja/upload/new&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;Content-Type&lt;/span&gt;&lt;span class="dl"&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;application/json&lt;/span&gt;&lt;span class="dl"&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;X-Api-Key&lt;/span&gt;&lt;span class="dl"&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;bws_...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&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;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newMetadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dragon Sword v5&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cid&lt;/span&gt; &lt;span class="p"&gt;}&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;uploadRes&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="c1"&gt;// 2. Re-point the IPNS name — tokenURI stays the same!&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.ipfs.ninja/ipns/publish&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;Content-Type&lt;/span&gt;&lt;span class="dl"&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;application/json&lt;/span&gt;&lt;span class="dl"&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;X-Api-Key&lt;/span&gt;&lt;span class="dl"&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;bws_...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&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;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;ipnsName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;k51qzi5uqu5dlvj2bv6...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cid&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No contract upgrade. No new CID to coordinate. Marketplaces and wallets that resolve IPNS will reflect the new state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 3: Connect your domain with DNSLink
&lt;/h2&gt;

&lt;p&gt;You can point a real domain at an IPNS name with a single TXT record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_dnslink.yourdomain.com  TXT  "dnslink=/ipns/k51qzi5uqu5dlvj2bv6..."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify with &lt;code&gt;dig&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig +short TXT _dnslink.myapp.com
&lt;span class="c"&gt;# "dnslink=/ipns/k51qzi5uqu5dlvj2bv6..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then access via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://ipfs.ninja/ipns/myapp.com
ipns://myapp.com   (in IPFS-aware browsers like Brave)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the TXT record is set, you never have to touch DNS again. Just publish new CIDs to the IPNS name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest gotchas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Propagation isn't instant.&lt;/strong&gt; Publishing to the DHT can take up to ~60 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Records expire after 48h&lt;/strong&gt; if not republished. We auto-republish every 12h to keep yours alive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inactive names&lt;/strong&gt; (no publishes for 90 days) stop being republished — publish anything once to reactivate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNSLink DNS changes&lt;/strong&gt; can take up to 24h to propagate globally. After that, switching CIDs is instant; you don't touch DNS again.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;IPNS docs: &lt;strong&gt;&lt;a href="https://ipfs.ninja/docs/api/ipns" rel="noopener noreferrer"&gt;ipfs.ninja/docs/api/ipns&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Sign up: &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;ipfs.ninja&lt;/a&gt;&lt;/strong&gt; (Free Dharma plan to test, IPNS unlocks on Bodhi at $5/mo)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're using IPNS in a way I didn't cover — collaborative docs, evolving on-chain art, machine-readable status pages — I'd love to hear about it in the comments. We're actively shaping the roadmap based on real use cases.&lt;/p&gt;

&lt;p&gt;— Nacho, BWS team&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>showdev</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>Pin Files to IPFS Straight from Claude Code, Cursor, and Windsurf (MCP Server)</title>
      <dc:creator>Nacho Coll</dc:creator>
      <pubDate>Sat, 25 Apr 2026 10:11:53 +0000</pubDate>
      <link>https://dev.to/nachocoll/pin-files-to-ipfs-straight-from-claude-code-cursor-and-windsurf-mcp-server-514e</link>
      <guid>https://dev.to/nachocoll/pin-files-to-ipfs-straight-from-claude-code-cursor-and-windsurf-mcp-server-514e</guid>
      <description>&lt;p&gt;Hi devs — Nacho here from the &lt;strong&gt;BWS (Blockchain Web Services)&lt;/strong&gt; team. We just shipped &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;IPFS.NINJA&lt;/a&gt;&lt;/strong&gt;, a managed IPFS pinning service, and one of the integrations I personally use the most is the &lt;strong&gt;MCP server&lt;/strong&gt; that lets you upload files, pin CIDs, and check storage usage &lt;strong&gt;directly from Claude Code, Cursor, or Windsurf&lt;/strong&gt; — just by asking the AI in plain English.&lt;/p&gt;

&lt;p&gt;Full disclosure: I work on this product. This post is a transparent walkthrough from the team that built it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What MCP gives you
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol (MCP)&lt;/a&gt; is an open standard for connecting AI coding assistants to external tools. Our MCP server exposes &lt;strong&gt;12 tools&lt;/strong&gt; the model can call mid-conversation against your IPFS.NINJA account:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File operations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ipfs_upload&lt;/code&gt; — Upload file content (base64 or text)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_upload_json&lt;/code&gt; — Upload a JSON object&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_import_car&lt;/code&gt; — Import a CAR file (DAG import)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_list&lt;/code&gt; — List your uploaded files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_get&lt;/code&gt; — Get file metadata by CID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_delete&lt;/code&gt; — Unpin and delete a file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pinning&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ipfs_pin&lt;/code&gt; — Pin an existing CID from the network&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_pin_status&lt;/code&gt; — Check pin progress&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Organization&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ipfs_folders_list&lt;/code&gt; / &lt;code&gt;ipfs_folders_create&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Account&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ipfs_profile&lt;/code&gt; — Plan, storage, bandwidth&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ipfs_analytics&lt;/code&gt; — Daily bandwidth and file stats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The net effect: you stop context-switching between your terminal, the dashboard, and your editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup for Claude Code (60 seconds)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Sign up at &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;ipfs.ninja&lt;/a&gt;&lt;/strong&gt; (free) and create an API key from Dashboard → API Keys. Copy it (it's only shown once).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Add the MCP server:&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 ipfs-ninja &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--transport&lt;/span&gt; stdio &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;IPFS_NINJA_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bws_your_full_api_key_here &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--&lt;/span&gt; npx &lt;span class="nt"&gt;-y&lt;/span&gt; @ipfs-ninja/mcp-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or add manually to &lt;code&gt;.claude/settings.json&lt;/code&gt;:&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;"mcpServers"&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;"ipfs-ninja"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@ipfs-ninja/mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&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;"IPFS_NINJA_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bws_your_full_api_key_here"&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;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;strong&gt;3.&lt;/strong&gt; Restart Claude Code. Type &lt;code&gt;/mcp&lt;/code&gt; to confirm &lt;code&gt;ipfs-ninja&lt;/code&gt; is connected.&lt;/p&gt;

&lt;p&gt;The npm package is &lt;code&gt;@ipfs-ninja/mcp-server&lt;/code&gt; — no global install needed, runs via &lt;code&gt;npx&lt;/code&gt;. Requires Node.js 18+.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup for Cursor / Windsurf
&lt;/h2&gt;

&lt;p&gt;In Settings → MCP Servers, add:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ipfs-ninja&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transport&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stdio&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Args&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-y @ipfs-ninja/mcp-server&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IPFS_NINJA_API_KEY=bws_...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What it actually feels like
&lt;/h2&gt;

&lt;p&gt;Once installed, you just talk to the assistant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: Upload my README.md to IPFS
You: List my recent files
You: How much storage am I using?
You: Pin bafyabc123... from the IPFS network
You: Create a folder called "project-assets"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model picks the right tool, calls our API, and returns a CID + a public gateway URL like &lt;code&gt;https://ipfs.ninja/ipfs/&amp;lt;CID&amp;gt;&lt;/code&gt;. No copy-pasting curl commands, no context switching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real workflows it unlocks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Deploy a static site to IPFS from Claude Code:&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;You: Upload the contents of my dist/ folder to IPFS
Claude: [uploads each file, returns CIDs]
You: What's the CID for index.html?
Claude: [calls ipfs_get] → QmXyz... — https://ipfs.ninja/ipfs/QmXyz...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NFT metadata pipeline:&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;You: Create a folder called "my-collection" and upload this metadata JSON
Claude: [calls ipfs_folders_create, then ipfs_upload_json]
        → Folder: my-collection
        → CID: QmAbc... — permanent metadata URL ready for your smart contract
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Monitor usage without leaving the editor:&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;You: Am I close to my storage limit?
Claude: [calls ipfs_profile]
        → Plan: Bodhi, Storage: 45.2 MB / 100 GB (0.04%)
You: Show my bandwidth this week
Claude: [calls ipfs_analytics with days=7]
        → 2.3 MB bandwidth, 45 requests across 3 days
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pin existing content from the network:&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;You: Pin the IPFS readme at QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG
Claude: [calls ipfs_pin] → Pin initiated! Status: pinning
You: Is it done?
Claude: [calls ipfs_pin_status] → Status: pinned, Size: 0.008 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting (the three things that actually go wrong)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;IPFS_NINJA_API_KEY environment variable is required&lt;/code&gt;&lt;/strong&gt; — the &lt;code&gt;env&lt;/code&gt; block in your MCP config is missing the key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;API error 402: not enough storage&lt;/code&gt;&lt;/strong&gt; — you've hit your plan's storage limit. Upgrade or delete unused files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server not showing in &lt;code&gt;/mcp&lt;/code&gt;&lt;/strong&gt; — you forgot to restart the editor after adding the server. Also check &lt;code&gt;node --version&lt;/code&gt; is ≥ 18.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;MCP Server docs: &lt;strong&gt;&lt;a href="https://ipfs.ninja/docs/api/mcp-server" rel="noopener noreferrer"&gt;ipfs.ninja/docs/api/mcp-server&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;npm package: &lt;code&gt;@ipfs-ninja/mcp-server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sign up free: &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;ipfs.ninja&lt;/a&gt;&lt;/strong&gt; (Dharma plan: 1 GB storage, 5 GB bandwidth/mo, all features)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build something cool with it (auto-pinning blog assets on commit, AI-driven NFT minting flows, etc.), I'd love to hear about it in the comments.&lt;/p&gt;

&lt;p&gt;— Nacho, BWS team&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>showdev</category>
      <category>web3</category>
    </item>
    <item>
      <title>Free On-the-Fly Image Optimization for IPFS Content (no node, no auth)</title>
      <dc:creator>Nacho Coll</dc:creator>
      <pubDate>Sat, 25 Apr 2026 10:10:43 +0000</pubDate>
      <link>https://dev.to/nachocoll/free-on-the-fly-image-optimization-for-ipfs-content-no-node-no-auth-1091</link>
      <guid>https://dev.to/nachocoll/free-on-the-fly-image-optimization-for-ipfs-content-no-node-no-auth-1091</guid>
      <description>&lt;p&gt;Hi everyone — Nacho here from the &lt;strong&gt;BWS (Blockchain Web Services)&lt;/strong&gt; team. We just shipped &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;IPFS.NINJA&lt;/a&gt;&lt;/strong&gt;, a managed IPFS pinning service, and I want to walk through one of the features I think is genuinely under-discussed in the IPFS world: &lt;strong&gt;on-the-fly image optimization&lt;/strong&gt; served straight from a public IPFS gateway.&lt;/p&gt;

&lt;p&gt;Full disclosure: I work on this product. This is a transparent walkthrough from the team that built it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;IPFS is great at serving immutable content. But if you've ever tried to use IPFS for the image-heavy parts of a real product — NFT galleries, dApp avatars, marketplace thumbnails, blog covers — you've hit the same wall:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your CID is the original 4 MB PNG. There is no &lt;code&gt;?w=400&lt;/code&gt; to ask for a thumbnail.&lt;/li&gt;
&lt;li&gt;You either pre-generate every variant at upload time (and now you're managing N CIDs per asset), or you serve the full-size file every time and watch your bandwidth quota burn.&lt;/li&gt;
&lt;li&gt;Modern formats like AVIF and WebP? You'd need to encode them yourself before uploading.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why most production dApps end up putting a Cloudinary or Imgix in front of IPFS, which kind of defeats the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we built
&lt;/h2&gt;

&lt;p&gt;IPFS.NINJA exposes a public, no-auth image transform endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /image/:cid
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query parameters:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Param&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;w&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Output width in pixels (max 4096)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;h&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Output height in pixels (max 4096)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;format&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;webp&lt;/code&gt;, &lt;code&gt;jpeg&lt;/code&gt;, &lt;code&gt;png&lt;/code&gt;, or &lt;code&gt;avif&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;quality&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;Compression quality 1-100 (only when &lt;code&gt;format&lt;/code&gt; is set)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cover&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;cover&lt;/code&gt;, &lt;code&gt;contain&lt;/code&gt;, &lt;code&gt;fill&lt;/code&gt;, &lt;code&gt;inside&lt;/code&gt;, or &lt;code&gt;outside&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It's served from &lt;code&gt;api.ipfs.ninja/image/:cid&lt;/code&gt;, available on &lt;strong&gt;all plans including the free Dharma tier&lt;/strong&gt; (no API key required for the transform itself), and the responses ship with immutable cache headers — same CID + same params = same bytes forever, perfect for any CDN in front.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick examples
&lt;/h2&gt;

&lt;p&gt;Resize to 400px wide as WebP:&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="s2"&gt;"https://api.ipfs.ninja/image/QmXmCX9S6ANV...?w=400&amp;amp;format=webp"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;200x200 JPEG thumbnail at 60% quality:&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="s2"&gt;"https://api.ipfs.ninja/image/QmXmCX9S6ANV...?w=200&amp;amp;h=200&amp;amp;format=jpeg&amp;amp;quality=60&amp;amp;fit=cover"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Drop it into HTML
&lt;/h2&gt;

&lt;p&gt;The whole point is that you can use it like any other image CDN. Here's a responsive &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"
    https://api.ipfs.ninja/image/QmXmCX9S6ANV...?w=400&amp;amp;format=webp 400w,
    https://api.ipfs.ninja/image/QmXmCX9S6ANV...?w=800&amp;amp;format=webp 800w,
    https://api.ipfs.ninja/image/QmXmCX9S6ANV...?w=1200&amp;amp;format=webp 1200w
  "&lt;/span&gt;
  &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://api.ipfs.ninja/image/QmXmCX9S6ANV...?w=800&amp;amp;format=webp"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Responsive IPFS image"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You store one CID. The browser asks for the size and format it actually needs. The bytes that come back are content-addressed back to your original via the CID in the URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters for NFTs
&lt;/h2&gt;

&lt;p&gt;If you mint with the original artwork's CID as the canonical reference (and you should — that's what gives the asset provenance), you can still render efficient gallery views, thumbnails, and previews:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`ipfs://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cid&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="c1"&gt;// canonical, immutable&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thumb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://api.ipfs.ninja/image/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?w=240&amp;amp;h=240&amp;amp;format=webp&amp;amp;quality=70`&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;preview&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://api.ipfs.ninja/image/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?w=800&amp;amp;format=avif`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original artwork stays untouched on IPFS. You just get cheap, on-demand variants for your UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on framework integration
&lt;/h2&gt;

&lt;p&gt;Next.js &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt;, Nuxt &lt;code&gt;&amp;lt;NuxtImg&amp;gt;&lt;/code&gt;, Astro &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt;, and most other framework image components let you plug in a custom loader. You can write a one-liner that maps &lt;code&gt;(src, width, quality)&lt;/code&gt; → &lt;code&gt;https://api.ipfs.ninja/image/${cid}?w=${width}&amp;amp;format=webp&amp;amp;quality=${quality}&lt;/code&gt; and you've effectively connected your framework's responsive image pipeline to IPFS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching
&lt;/h2&gt;

&lt;p&gt;Responses include immutable cache headers. Because IPFS content is content-addressed, the same CID with the same query parameters always produces the same output. Browsers and CDNs (Cloudflare, Fastly, your own Varnish) can cache these responses indefinitely — no cache busting needed, ever.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits and honesty
&lt;/h2&gt;

&lt;p&gt;A few things worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Max output dimensions: 4096px on either axis.&lt;/li&gt;
&lt;li&gt;The transform endpoint is public; if you don't want certain CIDs hot-reachable, don't pin them publicly (or use Nirvana's dedicated gateway with token-based access).&lt;/li&gt;
&lt;li&gt;If you don't pass any params, the request just redirects to the original file.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Image Optimization docs: &lt;strong&gt;&lt;a href="https://ipfs.ninja/docs/api/image-optimization" rel="noopener noreferrer"&gt;ipfs.ninja/docs/api/image-optimization&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Sign up free (Dharma plan, image optimization included): &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;ipfs.ninja&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions or edge cases you'd like to see supported (focal point cropping, smart blur for placeholders, etc.) — drop them below. We're actively iterating.&lt;/p&gt;

&lt;p&gt;— Nacho, BWS team&lt;/p&gt;

</description>
      <category>performance</category>
      <category>showdev</category>
      <category>web3</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Drop-in S3 for IPFS: Pin to IPFS.NINJA with the AWS SDK You Already Use</title>
      <dc:creator>Nacho Coll</dc:creator>
      <pubDate>Sat, 25 Apr 2026 10:09:22 +0000</pubDate>
      <link>https://dev.to/nachocoll/drop-in-s3-for-ipfs-pin-to-ipfsninja-with-the-aws-sdk-you-already-use-3139</link>
      <guid>https://dev.to/nachocoll/drop-in-s3-for-ipfs-pin-to-ipfsninja-with-the-aws-sdk-you-already-use-3139</guid>
      <description>&lt;p&gt;Hi devs — I'm Nacho, part of the &lt;strong&gt;BWS (Blockchain Web Services)&lt;/strong&gt; team. We just shipped &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;IPFS.NINJA&lt;/a&gt;&lt;/strong&gt;, a managed IPFS pinning service, and one of the features we're most excited about is the &lt;strong&gt;S3-compatible API&lt;/strong&gt;: you can keep using the AWS SDK you already know and pin straight to IPFS, getting a permanent CID back as the &lt;code&gt;ETag&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This post is a transparent walkthrough from the people who built it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why an S3 surface for IPFS?
&lt;/h2&gt;

&lt;p&gt;Most teams already have working code that talks to S3 (or to S3-compatible services like Filebase, R2, MinIO). When you decide to move some of that storage to IPFS for verifiability, decentralization, or NFT use cases, you usually have to learn a new API, change your upload flow, and rewrite tooling.&lt;/p&gt;

&lt;p&gt;We wanted to remove that friction. With IPFS.NINJA's S3 endpoint, you swap the &lt;code&gt;endpoint&lt;/code&gt; and &lt;code&gt;credentials&lt;/code&gt; and your existing &lt;code&gt;PutObject&lt;/code&gt; / &lt;code&gt;GetObject&lt;/code&gt; / &lt;code&gt;ListObjectsV2&lt;/code&gt; / &lt;code&gt;DeleteObject&lt;/code&gt; calls keep working — but every upload also gets pinned on IPFS and returns a content-addressed CID.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 30-second setup
&lt;/h2&gt;

&lt;p&gt;Endpoint: &lt;code&gt;https://s3.ipfs.ninja&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Your IPFS.NINJA API key acts as both the access key and the secret key:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;accessKeyId&lt;/code&gt;: first 12 chars of your key (e.g. &lt;code&gt;bws_628bba35&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secretAccessKey&lt;/code&gt;: the full key (e.g. &lt;code&gt;bws_628bba35e9e0079d9ff9c392b1b55a7b&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;region&lt;/code&gt;: &lt;code&gt;us-east-1&lt;/code&gt; (always)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;forcePathStyle: true&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;S3Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PutObjectCommand&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;@aws-sdk/client-s3&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;s3&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;S3Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&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://s3.ipfs.ninja&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bws_628bba35&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bws_628bba35e9e0079d9ff9c392b1b55a7b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;forcePathStyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;put&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;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-project&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&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;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;IPFS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CID:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;put&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// CID: QmXnnyufdzAWL5CqZ2RnSNgPbvCc1ALT73s6epPrRnZ1Xy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole thing. The CID comes back in the response metadata (and in the &lt;code&gt;ETag&lt;/code&gt;), and the file is immediately retrievable at &lt;code&gt;https://ipfs.ninja/ipfs/&amp;lt;CID&amp;gt;&lt;/code&gt; — or from any public IPFS gateway, since the network is decentralized.&lt;/p&gt;

&lt;h2&gt;
  
  
  Buckets = Folders
&lt;/h2&gt;

&lt;p&gt;S3 buckets map 1:1 to your IPFS.NINJA folders, so the mental model is exactly the same:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;S3 Operation&lt;/th&gt;
&lt;th&gt;IPFS.NINJA Equivalent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CreateBucket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new folder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ListBuckets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List your folders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PutObject&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Upload file into the folder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ListObjectsV2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List files in the folder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DeleteBucket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete a folder and all files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CreateBucketCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nft-metadata&lt;/span&gt;&lt;span class="dl"&gt;"&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;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nft-metadata&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;token-42.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&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;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My NFT #42&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The folders you create from the S3 API show up in your dashboard and can be managed from the REST API too — same underlying storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multipart uploads for big files
&lt;/h2&gt;

&lt;p&gt;The standard AWS multipart flow works out of the box (up to 5 GB):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;Upload&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;@aws-sdk/lib-storage&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="nx"&gt;fs&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;fs&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;upload&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;Upload&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-project&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;large-dataset.tar.gz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;large-dataset.tar.gz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/gzip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;partSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;httpUploadProgress&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="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;`Uploaded &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaded&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; of &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&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="s2"&gt; bytes`&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;result&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;upload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;done&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CID:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ETag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Python / Go also work
&lt;/h2&gt;

&lt;p&gt;Because it's plain S3, anything that speaks S3 works. Here's &lt;code&gt;boto3&lt;/code&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;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;endpoint_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://s3.ipfs.ninja&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_access_key_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bws_628bba35&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_secret_access_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;bws_628bba35e9e0079d9ff9c392b1b55a7b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-project&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;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;data.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;IPFS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&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;Same for &lt;code&gt;aws-sdk-go-v2&lt;/code&gt;, Rust S3 clients, &lt;code&gt;s3cmd&lt;/code&gt;, &lt;code&gt;rclone&lt;/code&gt;, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest differences from Amazon S3
&lt;/h2&gt;

&lt;p&gt;We want to be upfront about the parts that don't translate. Because IPFS is content-addressed and immutable:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Amazon S3&lt;/th&gt;
&lt;th&gt;IPFS.NINJA S3&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Storage model&lt;/td&gt;
&lt;td&gt;Mutable objects&lt;/td&gt;
&lt;td&gt;Content-addressed (immutable CIDs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overwrite behavior&lt;/td&gt;
&lt;td&gt;Replaces in place&lt;/td&gt;
&lt;td&gt;Creates a new CID; old CID still works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Versioning&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Use CIDs as version pointers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Presigned URLs&lt;/td&gt;
&lt;td&gt;Supported&lt;/td&gt;
&lt;td&gt;Use signed upload tokens instead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max object size&lt;/td&gt;
&lt;td&gt;5 TB&lt;/td&gt;
&lt;td&gt;5 GB (multipart), 100 MB (single PUT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regions&lt;/td&gt;
&lt;td&gt;Multi-region&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;us-east-1&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ETag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MD5&lt;/td&gt;
&lt;td&gt;IPFS CID&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last row is actually the magic: your &lt;code&gt;ETag&lt;/code&gt; is a real IPFS CID you can hand to anyone, anywhere on the network.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating from Amazon S3 or Filebase
&lt;/h2&gt;

&lt;p&gt;For Amazon S3, the diff is just the endpoint and credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;const s3 = new S3Client({
&lt;/span&gt;&lt;span class="gi"&gt;+ endpoint: "https://s3.ipfs.ninja",
&lt;/span&gt;  credentials: {
&lt;span class="gd"&gt;-   accessKeyId: "AKIA...",
-   secretAccessKey: "wJalrX..."
&lt;/span&gt;&lt;span class="gi"&gt;+   accessKeyId: "bws_628bba35",
+   secretAccessKey: "bws_628bba35e9e0..."
&lt;/span&gt;  },
  region: "us-east-1",
&lt;span class="gi"&gt;+ forcePathStyle: true
&lt;/span&gt;});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Filebase, only the endpoint changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- endpoint: "https://s3.filebase.com",
&lt;/span&gt;&lt;span class="gi"&gt;+ endpoint: "https://s3.ipfs.ninja",
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your existing &lt;code&gt;PutObject&lt;/code&gt;, &lt;code&gt;GetObject&lt;/code&gt;, &lt;code&gt;ListObjectsV2&lt;/code&gt;, &lt;code&gt;DeleteObject&lt;/code&gt; calls work unchanged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go next
&lt;/h2&gt;

&lt;p&gt;If you're building NFT metadata pipelines, static site deploys to IPFS, or just want pinning that doesn't require running a node, this is the fastest way to wire it into existing infrastructure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free tier (Dharma): 1 GB storage, 5 GB bandwidth/month, all features included&lt;/li&gt;
&lt;li&gt;Bodhi ($5/mo): 100 GB storage, 200 GB bandwidth, IPNS, dedicated gateways&lt;/li&gt;
&lt;li&gt;Nirvana ($29/mo): 1 TB storage, 10 dedicated gateways, IP whitelist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full S3 docs: &lt;strong&gt;&lt;a href="https://ipfs.ninja/docs/api/s3-compatibility" rel="noopener noreferrer"&gt;ipfs.ninja/docs/api/s3-compatibility&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Sign up free: &lt;strong&gt;&lt;a href="https://ipfs.ninja" rel="noopener noreferrer"&gt;ipfs.ninja&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Happy to answer questions about the design, limits, or migration paths in the comments — we read everything.&lt;/p&gt;

&lt;p&gt;— Nacho, BWS team&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8hgl6o3ruuhewpk0yzg8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8hgl6o3ruuhewpk0yzg8.png" alt=" " width="800" height="893"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>aws</category>
      <category>showdev</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
