> ## Documentation Index
> Fetch the complete documentation index at: https://developers.nateq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# حزمة Node.js

> أرسل واقرأ البريد الإلكتروني من Node.js، مع أنواع TypeScript وبدون أي اعتماديات وقت التشغيل.

عميل Node.js الرسمي لواجهة برمجة تطبيقات Nateq. مبني على TypeScript، ويدعم كلاً
من ESM و CommonJS، وبدون أي اعتماديات وقت التشغيل.

<CardGroup cols={2}>
  <Card title="npm" icon="package" href="https://www.npmjs.com/package/@nateq/sdk">
    @nateq/sdk
  </Card>

  <Card title="GitHub" icon="git-branch" href="https://github.com/nateq-ai/sdk-node">
    الكود المصدري والمشكلات
  </Card>
</CardGroup>

## المتطلبات

Node.js الإصدار 18 أو أحدث.

## التثبيت

```bash theme={null}
npm install @nateq/sdk
```

## البدء السريع

أنشئ مفتاح API بنطاق `emails:send`، ثم ضعه في بيئتك:

```bash theme={null}
NATEQ_API_KEY=tg_live_your_key_here
```

```ts theme={null}
import { Nateq } from "@nateq/sdk";

const nateq = new Nateq(); // reads NATEQ_API_KEY

const result = await nateq.emails.send({
  toEmails: ["customer@example.com"],
  subject: "Welcome aboard",
  htmlBody: "<p>Thanks for signing up.</p>",
});

console.log(result.id);
```

## المصادقة

تقرأ الحزمة `NATEQ_API_KEY` افتراضياً. مرّره صراحةً لتحميله من مدير أسرار، أو
للتعامل مع أكثر من مؤسسة:

```ts theme={null}
const nateq = new Nateq({ apiKey: await secrets.get("nateq_api_key") });
```

يتم التحقق من المفتاح محلياً قبل أي طلب، لذا يفشل المفتاح الخاطئ فوراً بدلاً من
إرساله عبر الشبكة.

<Warning>
  يحمل العميل مفتاح API الخاص بك، لذا لا تعرضه أبداً. تقوم `console.log` و
  `JSON.stringify` و `util.inspect` بإخفائه — لكن لا تُبطل ذلك بطباعة المفتاح
  مباشرةً.
</Warning>

| النطاق        | مطلوب لـ                         |
| ------------- | -------------------------------- |
| `emails:send` | `emails.send()`                  |
| `emails:read` | `emails.get()` و `emails.list()` |

## إرسال البريد الإلكتروني

تُنجَز `send()` بمجرد قبول الواجهة للرسالة. أما التسليم فيحدث بعد ذلك — الوعد
المُنجَز يعني أن Nateq تحمّلت مسؤولية الرسالة، لا أنها وصلت.

```ts theme={null}
const result = await nateq.emails.send({
  toEmails: ["a@example.com", "b@example.com"],
  subject: "Quarterly update",
  htmlBody: "<p>Hello</p>",
  plainTextBody: "Hello",
  fromEmail: "hello@yourdomain.com", // must be a verified address
  fromName: "Acme Support",
  ccEmails: ["c@example.com"],
  bccEmails: ["d@example.com"],
  replyTo: "support@yourdomain.com",
  ticketId: "…",
  attachmentIds: ["…"],
  headers: { "X-Campaign": "q3" },
});
```

قدّم `htmlBody` أو `plainTextBody` أو كليهما. احذف `fromEmail` و
`emailAddressId` لاستخدام عنوان مؤسستك الافتراضي المُوثَّق.

## قراءة البريد الإلكتروني

```ts theme={null}
const email = await nateq.emails.get(result.id);

email.status; // "delivered"
email.openCount;
email.bounceReason;
```

اعرض قائمة الرسائل المُرسلة، الأحدث أولاً:

```ts theme={null}
const page = await nateq.emails.list({
  status: "bounced",
  toEmail: "customer@example.com",
  limit: 25,
});

for (const email of page.emails) {
  console.log(email.subject);
}

page.total;
```

القيمة الافتراضية لـ `limit` هي 50 والحد الأقصى 100.

## الأخطاء

كل خطأ يرث من `NateqError`، لذا تكفي كتلة `catch` واحدة:

```ts theme={null}
import {
  NateqError,
  NateqPermissionError,
  NateqRateLimitError,
  NateqValidationError,
} from "@nateq/sdk";

try {
  await nateq.emails.send({ /* … */ });
} catch (err) {
  if (err instanceof NateqValidationError) {
    // 400/422 — the request was wrong. Don't retry it unchanged.
  } else if (err instanceof NateqPermissionError) {
    // 403 — e.g. the key lacks emails:send
    err.code; // "INSUFFICIENT_SCOPE"
  } else if (err instanceof NateqRateLimitError) {
    err.retryAfter; // seconds
  } else if (err instanceof NateqError) {
    err.status;
    err.requestId; // quote this to support
  }
}
```

| الخطأ                      | متى                                       |
| -------------------------- | ----------------------------------------- |
| `NateqValidationError`     | 400/422، أو وسائط خاطئة تُكتشف قبل أي طلب |
| `NateqAuthenticationError` | 401 — مفتاح مفقود أو غير صالح أو مُلغى    |
| `NateqPermissionError`     | 403 — المفتاح صالح لكنه غير مصرّح له      |
| `NateqNotFoundError`       | 404                                       |
| `NateqRateLimitError`      | 429 — يحمل `retryAfter`                   |
| `NateqServerError`         | 5xx                                       |
| `NateqTimeoutError`        | تجاوز الطلب المهلة المحددة                |
| `NateqConnectionError`     | DNS أو TLS أو المقبس أو انقطاع الاتصال    |
| `NateqConfigurationError`  | إعداد خاطئ للحزمة — يُطلَق قبل أي طلب     |

راجع [الأخطاء](/ar/errors) لرموز الواجهة الأساسية.

## إعادة المحاولة والإرسال المُكرر

تُعاد عمليات القراءة (`get` و `list`) تلقائياً عند انتهاء المهلة أو أخطاء 5xx أو
فشل الاتصال، مع تراجع تدريجي يحترم `Retry-After`.

**أما `send()` فلا.** لا تملك الواجهة مفتاح تكرار (idempotency key)، لذا فإن
عملية إرسال تفشل *بعد* وصولها إلى الخادم قد تكون قد أُرسلت فعلاً. إعادتها قد
تُرسل رسالتين إلى عميلك، والحزمة لن تتخذ هذا القرار نيابةً عنك.

الاستثناء هو **429**، الذي تُطلقه الواجهة قبل إرسال أي شيء — لذا تُعاد المحاولة،
لأن التكرار مستحيل.

إذا أطلقت `send()` خطأ `NateqTimeoutError` أو `NateqServerError`، فالنتيجة غير
معروفة فعلاً. تحقق منها قبل إعادة المحاولة:

```ts theme={null}
try {
  await nateq.emails.send({ toEmails: [user.email], subject, htmlBody });
} catch (err) {
  if (err instanceof NateqTimeoutError || err instanceof NateqServerError) {
    const recent = await nateq.emails.list({ toEmail: user.email, limit: 5 });
    // decide based on what's there
  }
}
```

## الإعدادات

```ts theme={null}
const nateq = new Nateq({
  apiKey: "tg_live_…",
  baseUrl: "https://api.nateq.io/api/v1",
  timeout: 30_000, // ms
  maxRetries: 2, // 0 disables
});
```

يجب أن يكون `baseUrl` عبر `https`، إلا إذا كان يشير إلى localhost للتطوير المحلي.

## الاستخدام مع أُطر العمل

<CodeGroup>
  ```ts Next.js theme={null}
  // app/api/welcome/route.ts
  import { Nateq } from "@nateq/sdk";

  const nateq = new Nateq();

  export async function POST(request: Request) {
    const { email } = await request.json();

    await nateq.emails.send({
      toEmails: [email],
      subject: "Welcome aboard",
      htmlBody: "<p>Thanks for signing up.</p>",
    });

    return Response.json({ ok: true });
  }
  ```

  ```ts NestJS theme={null}
  import { Module, Injectable } from "@nestjs/common";
  import { Nateq } from "@nateq/sdk";

  @Module({
    providers: [{ provide: Nateq, useFactory: () => new Nateq() }],
    exports: [Nateq],
  })
  export class NateqModule {}

  @Injectable()
  export class WelcomeMailer {
    constructor(private readonly nateq: Nateq) {}

    async send(email: string) {
      await this.nateq.emails.send({
        toEmails: [email],
        subject: "Welcome aboard",
        htmlBody: "<p>Thanks for signing up.</p>",
      });
    }
  }
  ```

  ```ts Express theme={null}
  import express from "express";
  import { Nateq } from "@nateq/sdk";

  const nateq = new Nateq();
  const app = express();

  app.post("/welcome", express.json(), async (req, res, next) => {
    try {
      await nateq.emails.send({
        toEmails: [req.body.email],
        subject: "Welcome aboard",
        htmlBody: "<p>Thanks for signing up.</p>",
      });
      res.json({ ok: true });
    } catch (err) {
      next(err);
    }
  });
  ```
</CodeGroup>

<Note>
  أنشئ العميل مرة واحدة وأعد استخدامه. فهو عديم الحالة وآمن للمشاركة.
</Note>

<Check>
  الإرسال داخل الطلب يربط زمن استجابتك بزمن استجابتنا. لأي شيء غير حرج، أرسل من
  مهمة في الخلفية أو من طابور بدلاً من ذلك.
</Check>
