# Motta og verifiser callbacken

Kjør en liten mottaker i Node eller Python, sjekk signaturen til hver callback med arbeidsområdets signeringshemmelighet, og les svarene formbase sender når en forespørsel avsluttes.

## Motta og verifiser callbacken

En forespørsel med en callbackUrl avsluttes med en signert POST til den URL-en: svarene når mottakeren sender inn, eller beskjed om at den utløp eller ble kansellert. Denne guiden kjører en mottaker på maskinen din, sjekker signaturen, og skriver ut svarene.

<p>
  Du trenger et API-token og et publisert skjema, som beskrevet i{' '}
  <a href="/no/guides/rest-api/send-a-request">Send en forespørsel med REST API-et</a> tidligere, og Node eller Python på maskinen din. Hva
  en callback bærer, og hvordan nye forsøk fungerer, er i <a href="/no/requests/callbacks">Callbacks og signering</a>.
</p>

<h2 id="secret">1. Kopier signeringshemmeligheten</h2>

<p>
  formbase signerer hver callback med arbeidsområdets signeringshemmelighet for forespørsler. Åpne <strong>OAuth og API-nøkler</strong> i
  arbeidsområdets sidepanel og finn kortet <strong>Signeringshemmelighet for forespørsler</strong>:
</p>

<p>Klikk kopiknappen og behold hemmeligheten i en miljøvariabel i terminalen der mottakeren din kommer til å kjøre:</p>

```
export FORMBASE_SIGNING_SECRET='rqs_...'
```

> ⚠️ **Ikke regenerer den**
> <p>
>     Den tredje knappen lager en ny hemmelighet, og den gamle slutter å virke med én gang, også for callbacker som allerede er underveis. Du
>     trenger den ikke for å følge denne guiden.
>   </p>

<h2 id="receiver">2. Skriv mottakeren</h2>

<p>
  Lagre <code>verify</code>-funksjonen fra <a href="/no/requests/callbacks#verify">Callbacks og signering</a> ved siden av mottakeren din:
  som <code>verify.mjs</code> for Node, siden den bruker <code>import</code>, eller som <code>verify.py</code> for Python. Mottakeren leser
  den rå kroppen, sjekker signaturen, og først da tolker den JSON-en:
</p>

  
    
```
import http from 'node:http'
const secret = process.env.FORMBASE_SIGNING_SECRET
http
  .createServer((req, res) => {
    const chunks = []
    req.on('data', (chunk) => chunks.push(chunk))
    req.on('end', () => {
      const rawBody = Buffer.concat(chunks).toString('utf8')
      const signature = req.headers['x-formbase-signature'] ?? ''
      if (!verifyFormbaseCallback(rawBody, signature, secret)) {
        console.log('rejected: bad signature')
        res.writeHead(401).end()
        return
      }
      const event = JSON.parse(rawBody)
      console.log(event.type, event.data.request.externalId, JSON.stringify(event.data.answers))
      res.writeHead(200).end()
    })
  })
  .listen(8787)
```

  
  
    
```
import json, os
from http.server import BaseHTTPRequestHandler, HTTPServer
from verify import verify_formbase_callback
SECRET = os.environ['FORMBASE_SIGNING_SECRET']
class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        raw_body = self.rfile.read(int(self.headers['Content-Length']))
        signature = self.headers.get('X-formbase-Signature', '')
        if not verify_formbase_callback(raw_body, signature, SECRET):
            print('rejected: bad signature', flush=True)
            self.send_response(401)
            self.end_headers()
            return
        event = json.loads(raw_body)
        print(event['type'], event['data']['request'].get('externalId'), event['data'].get('answers'), flush=True)
        self.send_response(200)
        self.end_headers()
HTTPServer(('', 8787), Handler).serve_forever()
```

  

<p>Hash kroppen nøyaktig slik den ankom. Å tolke den og gjøre den tilbake til JSON endrer bytene, og signaturen stemmer ikke lenger.</p>

<h2 id="tunnel">3. Kjør den og gi den en offentlig URL</h2>

<p>Start mottakeren:</p>

```
node receiver.mjs      # or: python3 receiver.py
```

<p>
  formbase kaller bare offentlige HTTPS-adresser, så <code>localhost</code> duger ikke. Mens du tester, gir en tunnel maskinen din én.{' '}
  <a href="https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/trycloudflare/">
    Cloudflares hurtigtunnel
  </a>{' '}
  trenger ingen konto. Kjør den i en ny terminal:
</p>

```
cloudflared tunnel --url http://localhost:8787
```

<p>
  Den skriver ut en adresse som <code>https://horn-cod-classics-arab.trycloudflare.com</code>. ngrok og lignende verktøy fungerer på samme
  måte. I produksjon, bruk serverens egen HTTPS-URL i stedet.
</p>

<h2 id="request">4. Send en forespørsel med en callback-URL</h2>

<p>
  Opprett en testforespørsel som i <a href="/no/guides/rest-api/send-a-request#create">forrige guide</a>, og legg til{' '}
  <code>callbackUrl</code> med tunnel-adressen. Bruk din egen skjema-ID:
</p>

```
curl -X POST https://api.formbase.so/api/v1 \
  -H "Authorization: Bearer $FORMBASE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "requests.create",
    "params": {
      "formId": "jx75hdx8vb5hy1x85gm17nqgkn8f674g",
      "recipient": { "email": "ada@acme.example", "name": "Ada Lovelace" },
      "prefill": { "company_name": "Analytical Engines Ltd" },
      "readonly": ["company_name"],
      "externalId": "supplier-2044",
      "idempotencyKey": "supplier-2044",
      "callbackUrl": "https://horn-cod-classics-arab.trycloudflare.com/formbase",
      "test": true
    }
  }'
```

<p>
  Bruk en ny <code>idempotencyKey</code>: samme nøkkel med en annen kropp blir avvist. Åpne <code>url</code> fra svaret, svar på skjemaet,
  og send inn.
</p>

<h2 id="see-it">5. Se callbacken ankomme</h2>

<p>Noen sekunder etter at du sender inn, skriver mottakeren ut hendelsestypen, din eksterne ID og svarene. Node-mottakeren skriver ut:</p>

```
request.completed supplier-2044 {"certificate_of_incorporation":[{"name":"certificate-of-incorporation.pdf","size":635,"type":"application/pdf","url":"https://api.formbase.so/api/storage/...",...}],"company_name":"Analytical Engines Ltd","contact_email":"ada@acme.example","do_you_accept_our_30_day_payment_terms":"yes","vat_number":"GB123456789"}
```

<p>
  For å se en avvisning, send mottakeren en hvilken som helst POST selv, for eksempel{' '}
  <code>curl -X POST -d '{}' http://localhost:8787</code>. Den har ingen gyldig signatur, så mottakeren skriver ut{' '}
  <code>rejected: bad signature</code> og svarer 401.
</p>

<h2 id="production">Før du går live</h2>

<ul>
  <li>
    <strong>Svar 2xx innen 10 sekunder.</strong> formbase merker callbacken levert på enhver 2xx. En 5xx, en 408, en 429 eller en
    tidsavbrudd får nye forsøk i om lag fire timer; enhver annen 4xx, som 401-en over, stopper forsøkene. Se{' '}
    <a href="/no/requests/callbacks#retries">Nye forsøk</a>.
  </li>
  <li>
    <strong>
      Forgren på <code>type</code>.
    </strong>{' '}
    Samme URL hører også <code>request.expired</code> og <code>request.canceled</code>, som ikke bærer noen svar.
  </li>
  <li>
    <strong>Håndter hver hendelse én gang.</strong> Et nytt forsøk bærer samme <code>id</code>. Lagre IDene du har håndtert og hopp over en
    gjentakelse.
  </li>
  <li>
    <strong>
      Sjekk <code>test</code>.
    </strong>{' '}
    En callback fra en testforespørsel har <code>"test": true</code>. Fjern <code>test</code> fra
    <code>requests.create</code> for ekte mottakere.
  </li>
</ul>

<p>
  Hvis mottakeren din var nede lenger enn forsøkene varer, er svarene ikke tapt. Les dem med <code>requests.get</code>, eller send
  callbacken på nytt med <a href="/no/developers/rest-api#requests-replay-callback">requests.replayCallback</a> eller fra{' '}
  <a href="/no/requests/managing-requests">Forespørsler-siden</a>.
</p>

<h2 id="next">Neste</h2>

<div class="not-prose grid gap-3 sm:grid-cols-2 mb-8">
  - [Callbacks og signering](/no/requests/callbacks) — Hele nyttelasten, de tre hendelsene, og tidsplanen for nye forsøk.
  - [API-metoder](/no/developers/rest-api) — Hver metode, med parameterne og svarene sine.
</div>
