# Quickstart

Source: https://www.pingtower.com/docs/getting-started/quickstart/

From nothing to an alert on your phone in five steps. You need a terminal, and the iOS app if you want the phone to ring.

<div class="docs-steps">


1. **Create an account and a tenant**

   Sign up in the [iOS app](/docs/ios/install/), or from the API. Signing up gives you a login; it does not create a tenant, so create one next. A tenant is a company or a team, and everything below lives inside it.

   ```sh
   PT=https://api.pingtower.com

   curl -s "$PT/v1/signup" -H 'Content-Type: application/json' \
     -d '{"email":"you@example.com","password":"…"}'
   # → {"account":{…},"token":"pts_…"}

   curl -s "$PT/v1/tenants" -H "Authorization: Bearer pts_…" \
     -H 'Content-Type: application/json' -d '{"name":"Acme"}'
   # → {"id":"<tid>","name":"Acme","role":"owner"}
   ```

2. **Mint a tenant API key**

   The key starts with `ptk_`, grants the whole data plane for that tenant, and is shown once. Treat it like a password.

   ```sh
   curl -s -X POST "$PT/v1/tenants/<tid>/keys" -H "Authorization: Bearer pts_…"
   # → {"id":"…","token":"ptk_<tenant>_…"}
   ```

3. **Create a project and a source**

   A project groups the things you monitor. A source is one thing that sends: an app, a host, a probe. Creating a source mints its ingest token, also shown once. `retain_logs` keeps the raw lines so you can read them behind an alert.

   ```sh
   API=ptk_…

   curl -s "$PT/v1/projects" -H "Authorization: Bearer $API" -d '{"name":"checkout"}'

   curl -s "$PT/v1/projects/checkout/sources" -H "Authorization: Bearer $API" \
     -d '{"name":"api","retain_logs":true}'
   # → {"name":"api","token":"pti_<tenant>_…"}
   ```

4. **Add a rule**

   This one opens an alert on the first error-level line and re-pages every fifteen minutes until someone acks.

   ```sh
   curl -s "$PT/v1/projects/checkout/rules" -H "Authorization: Bearer $API" \
     -d '{"name":"errors","match":{"min_level":400},"renotify_minutes":15}'
   ```

5. **Send a line and watch it become an alert**

   Ingest takes the source token, not the API key. The response tells you whether a rule matched.

   <div class="docs-tabs">
  <div role="tablist"><button type="button" role="tab" aria-selected="true">curl</button><button type="button" role="tab" aria-selected="false">Go</button><button type="button" role="tab" aria-selected="false">Python</button></div><div role="tabpanel"><div class="highlight"><pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>curl -s <span style="color:#a6e3a1">&#34;</span><span style="color:#f5e0dc">$PT</span><span style="color:#a6e3a1">/v1/ingest&#34;</span> -H <span style="color:#a6e3a1">&#34;Authorization: Bearer pti_&lt;tenant&gt;_…&#34;</span> <span style="color:#89b4fa">\
</span></span></span><span style="display:flex;"><span>  -d <span style="color:#a6e3a1">&#39;{&#34;message&#34;:&#34;payment gateway timeout&#34;,&#34;level&#34;:400,
</span></span></span><span style="display:flex;"><span><span style="color:#a6e3a1">       &#34;keys&#34;:{&#34;order&#34;:&#34;o_48113&#34;,&#34;gateway&#34;:&#34;stripe&#34;},&#34;tags&#34;:[&#34;billing&#34;]}&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#6c7086;font-style:italic"># → 202 {&#34;alerted&#34;:true,&#34;template_id&#34;:&#34;…&#34;}</span>
</span></span></code></pre></div></div><div role="tabpanel" hidden><div class="highlight"><pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>body, _ <span style="color:#89dceb;font-weight:bold">:=</span> json.<span style="color:#89b4fa">Marshal</span>(<span style="color:#f38ba8">map</span>[<span style="color:#f38ba8">string</span>]<span style="color:#f38ba8">any</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e3a1">&#34;message&#34;</span>: <span style="color:#a6e3a1">&#34;payment gateway timeout&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e3a1">&#34;level&#34;</span>:   <span style="color:#fab387">400</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e3a1">&#34;keys&#34;</span>:    <span style="color:#f38ba8">map</span>[<span style="color:#f38ba8">string</span>]<span style="color:#f38ba8">any</span>{<span style="color:#a6e3a1">&#34;order&#34;</span>: <span style="color:#a6e3a1">&#34;o_48113&#34;</span>, <span style="color:#a6e3a1">&#34;gateway&#34;</span>: <span style="color:#a6e3a1">&#34;stripe&#34;</span>},
</span></span><span style="display:flex;"><span>    <span style="color:#a6e3a1">&#34;tags&#34;</span>:    []<span style="color:#f38ba8">string</span>{<span style="color:#a6e3a1">&#34;billing&#34;</span>},
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>req, _ <span style="color:#89dceb;font-weight:bold">:=</span> http.<span style="color:#89b4fa">NewRequest</span>(<span style="color:#a6e3a1">&#34;POST&#34;</span>, <span style="color:#a6e3a1">&#34;https://api.pingtower.com/v1/ingest&#34;</span>, bytes.<span style="color:#89b4fa">NewReader</span>(body))
</span></span><span style="display:flex;"><span>req.Header.<span style="color:#89b4fa">Set</span>(<span style="color:#a6e3a1">&#34;Authorization&#34;</span>, <span style="color:#a6e3a1">&#34;Bearer &#34;</span><span style="color:#89dceb;font-weight:bold">+</span>os.<span style="color:#89b4fa">Getenv</span>(<span style="color:#a6e3a1">&#34;PINGTOWER_INGEST_TOKEN&#34;</span>))
</span></span><span style="display:flex;"><span>req.Header.<span style="color:#89b4fa">Set</span>(<span style="color:#a6e3a1">&#34;Content-Type&#34;</span>, <span style="color:#a6e3a1">&#34;application/json&#34;</span>)
</span></span><span style="display:flex;"><span>resp, err <span style="color:#89dceb;font-weight:bold">:=</span> http.DefaultClient.<span style="color:#89b4fa">Do</span>(req)
</span></span></code></pre></div></div><div role="tabpanel" hidden><div class="highlight"><pre tabindex="0" style="color:#cdd6f4;background-color:#1e1e2e;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#94e2d5">import</span> <span style="color:#fab387">os</span><span style="color:#89dceb;font-weight:bold">,</span> <span style="color:#fab387">requests</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>requests<span style="color:#89dceb;font-weight:bold">.</span>post(
</span></span><span style="display:flex;"><span>    <span style="color:#a6e3a1">&#34;https://api.pingtower.com/v1/ingest&#34;</span>,
</span></span><span style="display:flex;"><span>    headers<span style="color:#89dceb;font-weight:bold">=</span>{<span style="color:#a6e3a1">&#34;Authorization&#34;</span>: <span style="color:#a6e3a1">&#34;Bearer &#34;</span> <span style="color:#89dceb;font-weight:bold">+</span> os<span style="color:#89dceb;font-weight:bold">.</span>environ[<span style="color:#a6e3a1">&#34;PINGTOWER_INGEST_TOKEN&#34;</span>]},
</span></span><span style="display:flex;"><span>    json<span style="color:#89dceb;font-weight:bold">=</span>{<span style="color:#a6e3a1">&#34;message&#34;</span>: <span style="color:#a6e3a1">&#34;payment gateway timeout&#34;</span>, <span style="color:#a6e3a1">&#34;level&#34;</span>: <span style="color:#fab387">400</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#a6e3a1">&#34;keys&#34;</span>: {<span style="color:#a6e3a1">&#34;order&#34;</span>: <span style="color:#a6e3a1">&#34;o_48113&#34;</span>, <span style="color:#a6e3a1">&#34;gateway&#34;</span>: <span style="color:#a6e3a1">&#34;stripe&#34;</span>}, <span style="color:#a6e3a1">&#34;tags&#34;</span>: [<span style="color:#a6e3a1">&#34;billing&#34;</span>]},
</span></span><span style="display:flex;"><span>    timeout<span style="color:#89dceb;font-weight:bold">=</span><span style="color:#fab387">5</span>,
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div></div></div>


   The alert is now in the feed. In the app, tap it for the count, the level, first and last seen, and the line that fired it; swipe to ack. From a terminal:

   ```sh
   curl -s "$PT/v1/pull?cursor=0" -H "Authorization: Bearer $API"
   curl -s -X POST "$PT/v1/alerts/<id>/ack" -H "Authorization: Bearer $API"
   ```


</div>


<div class="docs-callout warn"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M8 2 1.5 13.5h13L8 2Z"/><path d="M8 6.5v3M8 11.5v.5"/></svg><div><span class="t">Levels are numbers</span>100 debug, 200 info, 300 warn, 400 error, 500 critical. <code>min_level: 400</code> means &ldquo;error and worse&rdquo;. Anything that can POST JSON can be a source; put variable data in <code>keys</code> so repeats collapse into one alert with a count.</div>
</div>


## Where to go next

<div class="docs-cards">
<a class="docs-card" href="/docs/service/rules/"><b>Rules in depth</b><span>Conditions on keys, fire-after windows, identifier grouping, escalation ladders.</span></a>
<a class="docs-card" href="/docs/service/integrations/"><b>Tell Slack too</b><span>Webhook, Slack and Telegram integrations with a per-event delivery log.</span></a>
<a class="docs-card" href="/docs/addons/"><b>Probe from outside</b><span>Install the addons agent on any box and threshold its measurements with the rule you just wrote.</span></a>
<a class="docs-card" href="/agents/"><b>Let an agent do it</b><span>The agent guide is the same setup written so an AI agent can run it end to end.</span></a>
</div>
