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

# Update Newsletter

> Update an existing newsletter

<Note>
  This endpoint requires authentication and the `updateNewsletter` permission.
</Note>

### Path Parameters

<ParamField path="newsletterId" type="string" required>
  The UUID of the newsletter to update
</ParamField>

### Request Body

<ParamField body="title" type="string" required={false}>
  Newsletter title
</ParamField>

<ParamField body="content" type="string" required={false}>
  Newsletter content
</ParamField>

<ParamField body="imageId" type="string" required={false}>
  ID of the uploaded image file
</ParamField>

<ParamField body="imageUrl" type="string" required={false}>
  External image URL if not using uploaded file
</ParamField>

<ParamField body="pdfId" type="string" required={false}>
  ID of the uploaded PDF file
</ParamField>

<ParamField body="pdfUrl" type="string" required={false}>
  External PDF URL if not using uploaded file
</ParamField>

<ParamField body="status" type="string" required={false}>
  Newsletter status. One of: `draft`, `scheduled`, `published`, `archived`
</ParamField>

<ParamField body="publishDate" type="string" required={false}>
  Scheduled publication date (ISO 8601)
</ParamField>

### Response

<ResponseField name="id" type="string">
  Unique identifier for the newsletter (UUID)
</ResponseField>

<ResponseField name="title" type="string">
  Newsletter title
</ResponseField>

<ResponseField name="content" type="string">
  Newsletter content
</ResponseField>

<ResponseField name="image" type="object" required={false}>
  Uploaded image file information

  <Expandable title="Image Object">
    <ResponseField name="id" type="string">
      File ID
    </ResponseField>

    <ResponseField name="url" type="string">
      File URL
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="imageUrl" type="string" required={false}>
  External image URL if not using uploaded file
</ResponseField>

<ResponseField name="pdf" type="object" required={false}>
  Uploaded PDF file information

  <Expandable title="PDF Object">
    <ResponseField name="id" type="string">
      File ID
    </ResponseField>

    <ResponseField name="url" type="string">
      File URL
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="pdfUrl" type="string" required={false}>
  External PDF URL if not using uploaded file
</ResponseField>

<ResponseField name="hasPdf" type="boolean">
  Whether the newsletter has an associated PDF
</ResponseField>

<ResponseField name="status" type="string">
  Newsletter status. One of: `draft`, `scheduled`, `published`, `archived`
</ResponseField>

<ResponseField name="publishDate" type="string" required={false}>
  Scheduled or actual publication date (ISO 8601)
</ResponseField>

<ResponseField name="recipientCount" type="integer">
  Number of times the newsletter has been viewed/received
</ResponseField>

<ResponseField name="createdAt" type="string">
  Creation timestamp (ISO 8601)
</ResponseField>

<ResponseField name="updatedAt" type="string">
  Last update timestamp (ISO 8601)
</ResponseField>

<ResponseField name="createdBy" type="string">
  UUID of the user who created the newsletter
</ResponseField>

### Error Responses

<ResponseField name="error" type="object">
  Error details when the request fails

  <Expandable title="Error Object">
    <ResponseField name="status" type="integer">
      HTTP status code
    </ResponseField>

    <ResponseField name="reason" type="string">
      Error message explaining why the request failed
    </ResponseField>
  </Expandable>
</ResponseField>

<Note>
  Common error cases:

  * 400 Bad Request: Invalid newsletter ID format or validation error
  * 401 Unauthorized: Missing or invalid access token
  * 403 Forbidden: Missing required permission
  * 404 Not Found: Newsletter not found
</Note>

<RequestExample>
  ```bash cURL theme={null}
  curl -X PUT "https://api.tktchurch.com/v1/newsletters/123e4567-e89b-12d3-a456-426614174000" \
    -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
    -H "Content-Type: application/json" \
    -d '{
      "title": "Updated January Church Updates",
      "content": "Welcome to our updated newsletter...",
      "imageId": "file-789",
      "pdfId": "file-012",
      "status": "published",
      "publishDate": "2024-01-01T12:00:00Z"
    }'
  ```

  ```javascript JavaScript theme={null}
  const updateNewsletter = async (accessToken, newsletterId, updates) => {
    const response = await fetch(
      `https://api.tktchurch.com/v1/newsletters/${newsletterId}`,
      {
        method: 'PUT',
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(updates)
      }
    );

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error.reason);
    }

    return response.json();
  };

  // Usage
  try {
    const newsletter = await updateNewsletter(
      'eyJhbGciOiJIUzI1NiIs...',
      '123e4567-e89b-12d3-a456-426614174000',
      {
        title: 'Updated January Church Updates',
        content: 'Welcome to our updated newsletter...',
        imageId: 'file-789',
        pdfId: 'file-012',
        status: 'published',
        publishDate: '2024-01-01T12:00:00Z'
      }
    );
    console.log('Updated newsletter:', newsletter);
  } catch (error) {
    console.error('Failed to update newsletter:', error.message);
  }
  ```

  ```swift Swift theme={null}
  struct UpdateNewsletterRequest: Codable {
      let title: String?
      let content: String?
      let imageId: String?
      let imageUrl: String?
      let pdfId: String?
      let pdfUrl: String?
      let status: String?
      let publishDate: Date?
  }

  func updateNewsletter(
      accessToken: String,
      newsletterId: UUID,
      updates: UpdateNewsletterRequest
  ) async throws -> Newsletter {
      var urlRequest = URLRequest(url: URL(string: "https://api.tktchurch.com/v1/newsletters/\(newsletterId)")!)
      urlRequest.httpMethod = "PUT"
      urlRequest.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
      urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
      
      let encoder = JSONEncoder()
      encoder.dateEncodingStrategy = .iso8601
      urlRequest.httpBody = try encoder.encode(updates)
      
      let (data, response) = try await URLSession.shared.data(for: urlRequest)
      guard let httpResponse = response as? HTTPURLResponse else {
          throw URLError(.badServerResponse)
      }
      
      if httpResponse.statusCode != 200 {
          let error = try JSONDecoder().decode(ErrorResponse.self, from: data)
          throw error
      }
      
      let decoder = JSONDecoder()
      decoder.dateDecodingStrategy = .iso8601
      return try decoder.decode(Newsletter.self, from: data)
  }

  // Usage
  do {
      let request = UpdateNewsletterRequest(
          title: "Updated January Church Updates",
          content: "Welcome to our updated newsletter...",
          imageId: "file-789",
          imageUrl: nil,
          pdfId: "file-012",
          pdfUrl: nil,
          status: "published",
          publishDate: ISO8601DateFormatter().date(from: "2024-01-01T12:00:00Z")
      )
      
      let newsletter = try await updateNewsletter(
          accessToken: "eyJhbGciOiJIUzI1NiIs...",
          newsletterId: UUID(uuidString: "123e4567-e89b-12d3-a456-426614174000")!,
          updates: request
      )
      print("Updated newsletter:", newsletter)
  } catch {
      print("Failed to update newsletter:", error.localizedDescription)
  }
  ```

  ```kotlin Kotlin theme={null}
  data class UpdateNewsletterRequest(
      val title: String? = null,
      val content: String? = null,
      val imageId: String? = null,
      val imageUrl: String? = null,
      val pdfId: String? = null,
      val pdfUrl: String? = null,
      val status: String? = null,
      val publishDate: String? = null
  )

  class NewsletterService(private val client: OkHttpClient) {
      suspend fun updateNewsletter(
          accessToken: String,
          newsletterId: String,
          updates: UpdateNewsletterRequest
      ): Newsletter {
          val requestBody = updates.toJson()
              .toRequestBody("application/json".toMediaType())

          val request = Request.Builder()
              .url("https://api.tktchurch.com/v1/newsletters/$newsletterId")
              .put(requestBody)
              .header("Authorization", "Bearer $accessToken")
              .build()

          client.newCall(request).execute().use { response ->
              if (!response.isSuccessful) {
                  val error = response.body?.string()?.fromJson<ErrorResponse>()
                  throw Exception(error?.reason ?: "Failed to update newsletter")
              }
              
              return response.body?.string()?.fromJson<Newsletter>()
                  ?: throw Exception("Empty response")
          }
      }
  }

  // Usage
  try {
      val request = UpdateNewsletterRequest(
          title = "Updated January Church Updates",
          content = "Welcome to our updated newsletter...",
          imageId = "file-789",
          pdfId = "file-012",
          status = "published",
          publishDate = "2024-01-01T12:00:00Z"
      )
      
      val newsletter = newsletterService.updateNewsletter(
          "eyJhbGciOiJIUzI1NiIs...",
          "123e4567-e89b-12d3-a456-426614174000",
          request
      )
      println("Updated newsletter: $newsletter")
  } catch (e: Exception) {
      println("Failed to update newsletter: ${e.message}")
  }
  ```

  ```typescript React Native theme={null}
  import axios from 'axios';

  interface UpdateNewsletterRequest {
    title?: string;
    content?: string;
    imageId?: string;
    imageUrl?: string;
    pdfId?: string;
    pdfUrl?: string;
    status?: 'draft' | 'scheduled' | 'published' | 'archived';
    publishDate?: string;
  }

  const updateNewsletter = async (
    accessToken: string,
    newsletterId: string,
    updates: UpdateNewsletterRequest
  ): Promise<Newsletter> => {
    try {
      const response = await axios.put(
        `https://api.tktchurch.com/v1/newsletters/${newsletterId}`,
        updates,
        {
          headers: {
            'Authorization': `Bearer ${accessToken}`,
            'Content-Type': 'application/json'
          },
        }
      );
      
      return response.data;
    } catch (error) {
      if (axios.isAxiosError(error)) {
        throw new Error(error.response?.data?.error?.reason || 'Failed to update newsletter');
      }
      throw error;
    }
  };

  // Usage
  try {
    const newsletter = await updateNewsletter(
      'eyJhbGciOiJIUzI1NiIs...',
      '123e4567-e89b-12d3-a456-426614174000',
      {
        title: 'Updated January Church Updates',
        content: 'Welcome to our updated newsletter...',
        imageId: 'file-789',
        pdfId: 'file-012',
        status: 'published',
        publishDate: '2024-01-01T12:00:00Z'
      }
    );
    console.log('Updated newsletter:', newsletter);
  } catch (error) {
    console.error('Failed to update newsletter:', error.message);
  }
  ```
</RequestExample>

<ResponseExample>
  ```json 200 Success theme={null}
  {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "title": "Updated January Church Updates",
    "content": "Welcome to our updated newsletter...",
    "image": {
      "id": "file-789",
      "url": "https://cdn.tktchurch.com/files/updated-image.jpg"
    },
    "imageUrl": null,
    "pdf": {
      "id": "file-012",
      "url": "https://cdn.tktchurch.com/files/updated.pdf"
    },
    "pdfUrl": null,
    "hasPdf": true,
    "status": "published",
    "publishDate": "2024-01-01T12:00:00Z",
    "recipientCount": 150,
    "createdAt": "2023-12-30T15:00:00Z",
    "updatedAt": "2023-12-31T10:30:00Z",
    "createdBy": "user-123e4567-e89b-12d3-a456-426614174000"
  }
  ```

  ```json 400 Bad Request theme={null}
  {
    "error": {
      "status": 400,
      "reason": "Invalid newsletter ID format"
    }
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "error": {
      "status": 401,
      "reason": "Invalid or expired access token"
    }
  }
  ```

  ```json 403 Forbidden theme={null}
  {
    "error": {
      "status": 403,
      "reason": "Missing required permission: updateNewsletter"
    }
  }
  ```

  ```json 404 Not Found theme={null}
  {
    "error": {
      "status": 404,
      "reason": "Newsletter not found"
    }
  }
  ```
</ResponseExample>
